20172328 2018-2019《Java軟件結構與數據結構》第八週學習總結

20172328 2018-2019《Java軟件結構與數據結構》第八週學習總結

概述 Generalization

本週學習了二叉樹的另外一種有序擴展?是什麼呢?你猜對了!ヾ(◍°∇°◍)ノ゙就是堆。本章將講解堆的鏈表實現and數組實現,以及往堆中添加元素或從堆中刪除元素的算法;還將介紹對的一些用途,包括基本使用和優先隊列。

教材學習內容總結 A summary of textbook

  • (heap)就是具備兩個附加屬性的一顆二叉樹:
    • 第一點:它是一顆徹底二叉樹 ,即葉子節點都在最後一層靠左側。
    • 第二點:對每一結點,它小於(大於)或等於其左孩子和右孩子。
  • 第二點中知足大於的條件下造成的是一個最大堆(大頂堆),反之則爲最小堆(小頂堆)
  • 最小堆將其最小元素存儲在該二叉樹的根處,且其跟的兩個孩子一樣仍是最小堆。
  • 堆中的操做:
    html

  • addElement操做
    • addElement方法將給定的Comparable元素添加到堆的恰當位置處,且維持該堆的徹底性屬性和有序屬性java

      • Little Tip:徹底樹你還記得嗎?徹底二叉樹就是全部葉子都位於h或者h-1層,其中h爲log2(n),且n爲樹中的元素數目,h層的全部葉子都位於該樹的左邊,那麼該樹被認爲是徹底的。
    • 堆就是一棵徹底的二叉樹,因此對於插入的新結點只存在一個正確的位置。要麼是h層左邊的下一個空位置,要麼是h+1層左邊的第一個位置(若是h層爲滿的話)。
    • 將新結點定位到正確的位置後,咱們就必須對這個堆進行排序來保持其有序屬性。方法就是對樹中的最末一片葉子進行跟蹤記錄,在一個addElement方法後,最末結點就被設定爲插入結點。也就是經過上溯樹來保證樹的有序性.
  • removeMin操做
    • removeMin方法將刪除最小堆中的最小元素並返回它,因爲最小元素是存儲在最小堆的根處,因此咱們須要返回儲存在根處的元素並用堆中的另外一元素替換它。
    • 要維持該樹的徹底性,就是把儲存在樹中最末一片葉子上的元素用來替換根元素。
    • 將存儲在最末一片葉子上的元素移動到根處,就必須對該堆進行從新排序來維持該堆的排序屬性。
    • 要維持該堆的排序屬性,就要從根結點下溯樹。具體過程是將該新根元素與其較小的孩子進行比較,且若是孩子更小就將它們互換,沿着樹向下繼續這一過程,直至該元素要麼位於某一葉子中,要麼比它的兩個孩子都小。
  • findMin操做
    • findMin將返回一個指向最小堆中最小元素的引用,因爲最小堆的最小元素就在根處,因此直接返回存儲在根處的元素便可。
  • 使用堆——堆排序heapSort
    • 堆排序是對簡單選擇排序的一種改進。
    • 改進着眼點:如何減小關鍵字的比較次數
      • 簡單選擇排序在一趟排序中僅選出最小關鍵字,可是沒有把一趟比較結果保存下來,於是比較次數較多。
      • 堆排序在選出最小關鍵字同時,也找出較小的關鍵字,從而減小了後面選擇中的比較次數,提升了整個排序效率。
  • 左右子樹都是大頂堆,如何調整根結點,使得整棵樹成爲一個堆?
  • 堆調整 —— 篩選過程
    • 調整過程當中,老是將根結點(被調整結點)與左右孩子比較;
    • 不知足堆條件時,將根結點與左右孩子中較大者交換;
    • 這個調整過程一直進行到全部子樹都是堆或者交換到葉子爲止。
    • 這個從堆頂到葉子的調整過程稱爲「篩選」。
  • 對堆進行篩選完畢以後咱們就要爲保持其有序性而開始排序。
  • 堆排序-排序過程


    • 從無序序列的第[n/2]個元素開始(對應於徹底二叉樹的最後一個非終端結點)進行篩選;
    • 當解決一個非終端結點(非葉子結點)的有序問題後,把此非終端結點看作一個葉子,而後繼續上溯,找到倒數第二個非終端結點,繼續進行有序替換,知道整個堆有序爲止。
  • heapSort的複雜度爲O(nlogn)
  • 使用堆:優先級隊列
  • 優先級隊列(Priority queue)就是遵循兩個排序規則的集合。
  • 用鏈表實現堆
    • 由於咱們要求在插入元素後可以向上遍歷該樹,因此堆中的結點必須存儲在指向其雙親的指針。因此咱們從建立一個HeapNode類開始咱們的鏈表實現,該類對BinaryTreeNode進行了拓展並添加了一個雙親指針。
    • 鏈表實現的實例數據由指向HeapNode且稱爲lastNode的單個引用組成,這樣咱們就可以跟蹤記錄該堆中最末一片葉子。public HeapNode lastNode;
    • addElement操做:
      • 在恰當的位置添加一個新元素
      • 對堆進行重排序以維持其排序屬性
      • 將lastNode指針從新設定指向新的最末結點
    • removeMin操做:
      • 用存儲在最末結點處的元素替換存儲在根處的元素
      • 對堆進行重排序
      • 返回原始的根元素
    • findMin操做:
      • 該方法僅僅返回一個指向存儲在堆根處元素的引用,所以複雜度爲O(1)。
  • 用數組實現堆
    • 用數組實現堆再也不須要建立一個HeapNode類對,而是經過用數組來保存堆中的數據。
    • 在二叉樹的數組實現中,樹的根位於位置0處,對於每一結點n,n的左孩子將位於數組的2n+1處,右孩子將位於數組的2n+2處。
    • addElement操做:
      • 在恰當的位置添加一個新元素
      • 對堆進行重排序以維持其排序屬性
      • 將count遞增1.
    • removeMin操做:
      • 用存儲在最末結點處的元素替換存儲在根處的元素
      • 對堆進行重排序
      • 返回原始的根元素
    • findMin操做:
      • 返回指向存儲在堆的根處或數組0位置處的元素。
  • 總結:鏈表實現和數組實現的addElement方法和removeElement方法時間複雜度都是O(logn)。可是,數組的實現仍是更高效一些,由於在addElement操做中數組實現不用去肯定插入雙親的結點,在removeElement操做中數組實現不須要肯定新的最末一片葉子。git

教材學習中的問題和解決過程 Problem and countermeasure

  • 問題1:週四晚上看用鏈表實現堆的添加操做代碼。剛剛看懂小夥伴仇夏來找我討論,討論了一會瞬間以爲本身沒有真正看懂,那段奇妙的代碼以下:
private HeapNode<T> getNextParentAdd()
    {
        HeapNode<T> result = lastNode;

        while ((result != root) && (result.getParent().getLeft() != result))
            result = result.getParent();

        if (result != root)
            if (result.getParent().getRight() == null)
                result = result.getParent();
            else
            {
                result = (HeapNode<T>)result.getParent().getRight();
                while (result.getLeft() != null)
                    result = (HeapNode<T>)result.getLeft();
            }
        else
            while (result.getLeft() != null)
                result = (HeapNode<T>)result.getLeft();
          
        return result;
    }

這段代碼是返回將要插入結點的雙親結點的方法,當時我和仇夏討論的是有兩種狀況。由於咱們知道插入操做爲了保證樹的徹底性,因此要在h層的從左開始數的第一個空結點插入或者在h+1層的最左邊插入(若是樹是滿的話),可是在循環體中不知足咱們的要求。程序員

  • 問題1的解決:
  • 問題的重點是「下一個將要插入結點的雙親結點」,以前一直理解的是新插入結點的雙親結點,因此固然不對。在理解正確以後,畫出幾種插入的可能狀況,再跟着上述代碼理解幾遍,就慢慢清晰明朗了。
    算法

  • 問題2:以前在學習棧的時候感受一直在和堆一塊兒被提起,因此堆和棧之間究竟是什麼關係?
  • 問題2的解決:
  • 簡單的說: Java把內存劃分紅兩種:一種是棧內存,一種是堆內存。
  • 在函數中定義的一些基本類型的變量和對象的引用變量都在函數的棧內存中分配。 當在一段代碼塊定義一個變量時,Java就在棧中爲這個變量分配內存空間,當超過變量的做用域後,Java會自動釋放掉爲該變量所分配的內存空間,該內存空間能夠當即被另做他用。
  • 堆內存用來存放由new建立的對象和數組。在堆中分配的內存,由Java虛擬機的自動垃圾回收器來管理。
  • 堆和棧的區別比較:
    • 1.棧(stack)與堆(heap)都是Java用來在Ram中存放數據的地方。與C++不一樣,Java自動管理棧和堆,程序員不能直接地設置棧或堆。
    • 2.棧的優點是,存取速度比堆要快,僅次於直接位於CPU中的寄存器。但缺點是,存在棧中的數據大小與生存期必須是肯定的,缺少靈活性。另外,棧數據能夠共享,詳見第3點。堆的優點是能夠動態地分配內存大小,生存期也沒必要事先告訴編譯器,Java的垃圾收集器會自動收走這些再也不使用的數據。但缺點是,因爲要在運行時動態分配內存,存取速度較慢。
    • 3.Java中的數據類型有兩種。 一種是基本類型(primitive types), 共有8種,即int, short, long, byte, float, double, boolean, char(注意,並無string的基本類型)。這種類型的定義是經過諸如int a = 3; long b = 255L;的形式來定義的,稱爲自動變量。值得注意的是,自動變量存的是字面值,不是類的實例,即不是類的引用,這裏並無類的存在。如int a = 3; 這裏的a是一個指向int類型的引用,指向3這個字面值。這些字面值的數據,因爲大小可知,生存期可知(這些字面值固定定義在某個程序塊裏面,程序塊退出後,字段值就消失了),出於追求速度的緣由,就存在於棧中。

代碼實現時的問題做答 Exercise

  • 問題1:用堆實現隊列?樹中給的堆是由數組和鏈表實現的,那麼咱們應該用ArrayHeap仍是Linkedheap實現呢?又該如何實現?
  • 問題1的解決:首先,咱們知道在第十章中隊列的實現能夠由鏈表或者循環數組實現,而且堆能夠由鏈表和數組實現,所以是否能夠推出隊列能夠由數組實現的堆或者鏈表實現的堆來實現呢?
  • 首先,我先用鏈表實現的堆來實現隊列。經過在enqueue方法中調用addElement方法,就可以實現隊列的入隊,可是在入隊後進行了堆的插入中的排序?隊列是符合先進先出原則的,那麼我就修改了堆中的插入方法,將其中的排序刪掉。同理,當出隊時也不該該去考慮排序的問題。可是當我刪除掉刪除的排序後,再執行刪除隊列中前兩個元素,我卻發現刪除的並不必定是隊列的前兩個值,反而刪除掉了最開始跟的值和最末一個葉子,這是由堆的性質決定的。因此想要保持先進先出,咱們就要找到堆頂的左孩子而後刪除。因此咱們須要改變一下堆中的排序方法。鏈表的實現方法很麻煩,並且要改變刪除的排序算法須要考慮不少種狀況,在作了兩小時仍是不能完整正確的狀況下,讓咱們把目光投向數組。
    spring

  • 數組中只須要將刪除後的最末結點變成null就能夠達到效果。
    數組

上週測試活動錯題改正 Correction

  • 1.In removing an element from a binary search tree, another node must be ___________ to replace the node being removed.
    A .duplicated
    B .demoted
    C .promoted
    D .None of the above
  • 正確答案選 B。當在二叉查找樹上刪除一個結點時,後面的結點須要向上移動來補全。當時,覺得越靠近根結點說明深度越低,因此是降級了;可是看完答案好像人家的意思是向上補全。題意理解不許確哈哈
  • 2.In removing an element from a binary search tree, another node must be demoted to replace the node being removed.
    A .True
    B .Flase
  • 正確答案選 A ,和第一題如出一轍好嗎?哭
  • 3.One of the uses of trees is to provide simpler implementations of other collections.
    A .True
    B .Flase
  • 正確答案選 B 。這道題確實是我本身不太懂的。經過搜索得知,java中樹的應用主要有:菜單樹,還有權限樹,商品分類列表也是樹結構。在這幾章的學習中,好像確實沒有經過樹來簡單實現其餘集合,樹提供的方便性能夠用於查找、排序等等。
  • 4.Insertion sort is an algorithm that sorts a list of values by repetitively putting a particular value into its final, sorted, position.
    A .true
    B .false
  • 正確答案選 B ,我當時選了A。我以爲仍是題意理解的問題。我理解的題意是「插入排序是一種重複的將特殊值插入後面,而後排序,而後安置的算法」我以爲插入排序就是這樣一個流程,因此我也不知道出現了什麼問題。

碼雲連接

代碼量(截圖)

結對及互評Group Estimate

點評模板:

  • 博客中值得學習的或問題:
    • 20172301:這周的實驗四徹底沒有思路,是最後看了郭愷同窗的做業勉強理解了而後寫的。因此說,佩服佩服。對鏈表堆和數組堆的優缺點分析的很透徹。
    • 20172304:圖不少,可是排版有點混亂,不是特別美觀能夠繼續改進。內容仍是有點少,沒有上週博客好喲!一塊兒繼續加油!

其餘(感悟、思考等,可選)Else

Crossing miles of frustrations and rivers a raging,picking up stones I found along the way.數據結構

怕不輕鬆,怕過輕鬆。
不再用以爲工做日長,由於天天好像都是工做日。想忙裏偷閒就忙裏偷閒,想抓緊作事就抓緊作事。ide

我將天天告訴本身:請對專業課程更加虔誠一點。

學習進度條Learning List

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積)
目標 5000行 30篇 400小時
第一週 0/0 1/1 8/8
第二週 621/621 1/2 12/20
第三週 678/1299 1/3 10/30
第四周 2734/4033 1/4 20/50
第五週 1100/5133 1/5 20/70
第六週 1574/6707 2/7 15/85
第七週 1803/8510 1/8 20/105
第八週 2855/11365 2/10 25/130

參考資料Reference

相關文章
相關標籤/搜索