1. 程式人生 > >20172305 2018-2019-1 《Java軟體結構與資料結構》第八週學習總結

20172305 2018-2019-1 《Java軟體結構與資料結構》第八週學習總結

20172305 2018-2019-1 《Java軟體結構與資料結構》第八週學習總結

教材學習內容總結

本週內容主要為書第十二章內容:

  • (附加屬性的二叉樹)
    • 完全二叉樹
    • (最小堆)對於每一個結點,它小於或等於其左孩子和右孩子。
    • (最大堆)對於每一個結點,它大於或等於其左孩子和右孩子。


  • 最小堆將其最小元素儲存在二叉樹的根處,其根的兩個孩子同樣也是最小堆。
  • 最大堆將其最大元素儲存在二叉樹的根處,其根的兩個孩子同樣也是最大堆。

  • addElement方法將給定的元素新增到堆中的恰當位置處,且維持該堆的完全性屬性和有序屬性。
    • 如果給定元素不是Comparable的,則該方法將會丟擲一個ClassCastException異常。
    • 對於新插入結點位置要麼是第h層左邊的下一個空位置,要麼是第h+1層左邊的第1個位置(h層滿的話)。
    • 考慮排序屬性,將該新值和雙親值進行比較,如果新結點小於雙親則互換位置,沿著樹向上繼續此過程,直至該新結點要麼是大於雙親要麼是位於堆的根處。

      新增元素0:



  • removeMin方法將刪除最小堆中的最小元素並返回它。
    • 由於刪除元素是儲存在最小堆的根處,所以要返回儲存在根處元素並用堆中的最末一片葉子上的元素替代。一旦最末一片葉子的元素移動到根處,則必須要對堆進行重新排序以維持堆的排序屬性。
    • 新根元素與較小的子結點進行比較,如果子結點更小就替換,沿著樹向下這一過程,直至該元素要麼位於某片葉子要麼比兩個子結點都小。




  • findMin方法將返回一個指向最小堆中的最小元素的引用,只需通過返回儲存在根處的元素。

  • 三種方法的時間複雜度:

  • 堆--優先順序佇列(遵循兩個排序規則的集合,具有更高階的專案在先,具有相同優先順序的專案使用先進先出的方法來確定其排序)

  • 連結串列實現堆:
    • addElement操作使用了兩個私有方法getNextParentAdd和heapifyAdd兩個方法,getNextParentAdd方法是返回一個指向某結點的引用,該結點為插入結點的雙親。heapifyAdd方法是完成堆的任何重排序,從那片新葉子開始,向上處理至根處。

      • 在恰當位置處新增一個新元素,對堆進行重排序來維持排序屬性,將lastNode指標重新設定為指定新的最末結點。
    • removeMin操作使用了兩個私有方法getNewLastNode和heapifyRemove兩個方法,getNewLastNode方法返回一個指向某一結點的引用,該結點將是新的最末結點。heapifyRemove方法將完成任何有必要的樹重排序,從根向下進行。

      • 用儲存在最末結點處的元素替換儲存在根處的元素,對堆進行重排序,以及返回初始的根元素。
    • findMin方法返回一個指向儲存在根結點的元素的內容。

  • 陣列實現堆:
    • addElement操作使用了私有方法heapifyAdd方法,用於在必要時對該堆進行重組。

      • 在恰當位置處新增新結點,對堆進行重排序來維持排序屬性,將count遞增。
    • removeMin操作使用了私有方法heapifyRemove方法,用於在必要時對該堆進行重組。

      • 用儲存在最末結點處的元素替換儲存在根處的元素,對堆進行重排序,以及返回初始的根元素。
    • findMin方法返回一個指向儲存在根結點的元素的內容。

  • 堆排序

    • 將列表的每一個元素新增到堆中,然後在一次一個的將他們從根中刪除。
    • 最小堆的情況下,排序結果是將該列表以升序排列
    • 最大堆的情況下,排序結果是將該列表以降序排列
    • 時間複雜度:O(nlogn)
    • 空間複雜度:O(1)

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

  • 問題1:實現優先順序佇列的二叉堆、d堆、左式堆、斜堆、二項堆都是什麼意思?和最小堆、最大堆有什麼區別?
  • 問題1解決方案:先說後面的問題,最小堆和最大堆是二叉堆的兩種表現形式,最大堆是除了根以外的所有結都要滿足:父結點大於子結點;而最小堆恰恰相反,最小堆是除了根以外的所有結都要滿足:父結點小於子結點。所以,二叉堆可以根據優先順序確定出佇列的先後順序。

    • d堆是完全d叉樹,就是每一個結點有d個子結點,同時還要滿足每個結點從左到右的順序。
    • 左式堆是具有堆序性質的二叉樹,左式堆的任意結點的值比其子樹的任意結點值均小,但和一般的二叉堆不同,左式堆不再是一棵完全二叉樹。
    • 斜堆也叫自適應堆,是一種使用二叉樹實現的堆狀資料結構,使用的是二叉樹而不是完全二叉樹,所以在整體來看,斜堆會是極不平衡的一個堆,但其合併的速度遠遠大於二叉堆。
    • 二項堆是是二項樹的集合或是由一組二項樹組成,在O(logn)的時間內即可完成兩個二項堆合併操作,所以二項堆是可合併堆,基於二項堆實現的優先佇列和程序排程演算法有著很好的時間效能。

程式碼學習中的問題和解決過程

  • 問題1:如何解決陣列實現的堆在刪除操作之後會輸出被刪元素?分析陣列實現刪除新增操作的方法?
  • 問題1的解決方案:刪除一個元素之後,進行整體的輸出會出現被刪除元素,通過對程式碼的分析,在刪除最小結點後將末葉結點放到根處之後,沒有將葉結點內容進行刪除,而是在計數變數和操作次數進行遞減,這樣的話葉結點和暫時的根結點樹都會有同一元素。所以在tree[0]=tree[count - 1];heapifyRemove()之間新增一句tree[count - 1] = null;直接令其為空。針對刪除時對堆的排序方法(向下遍歷),先判斷根結點的左側和右側是否為空,如果為空的話就直接跳過迴圈讓新的根結點為根結點,如果不為空的話會有兩種情況,只有一個子結點,此時右側為空,則左側不為空,記錄此時左結點的索引值,如果左右都不為空的話進行判斷左側和右側誰更小(最小堆)。進入迴圈,迴圈條件是索引值小於總共數量和索引值位置與新根結點的比較,node始終是next的父結點的索引值,不斷向下遍歷,而對於每一個結點他的索引值是n的話,其左子結點為2n+1,右子結點為2(n+1),和之前操作類似,判斷來年兩個子結點的大小,向下不斷遍歷,next的值賦給node,並找node的孩子的索引,令next重新指向node的孩子直至不滿足next索引值超過總的或是元素大小不符合的時候會跳出迴圈,node的位置就是替代新結點的位置。針對新增時對堆的排序方法(向上排序),新增的方法進行的排序比較簡單,直接確定新增的位置是否比父結點小,如果小的話,進行調換;大得話跳出迴圈結束排序。和之前的類似,子結點的索引值為count-1的話,那麼其父結點位置為next-1/2,不斷的和父結點進行比較直至向上遍歷結束,當跳出迴圈後,此時的next位置就是新添結點的位置。

    未加tree[count - 1] = null

    新增之後:

  • 問題2:PP12.1如何讓用堆來實現佇列?佇列的方法如下:
  • 問題2的解決方案:用堆來實現佇列的問題,因為佇列是一個線性結構,先進先出,而堆是完全二叉樹。所以,需要根據把每一個進行佇列的元素標上號,按照號碼牌的內容來實現堆的形式,但是如果這樣的話,我們就不能直接往堆裡面插入泛型元素,需要定義個類,而在這個類的內部需要每定義一個就會自增數量,比如先進的是A元素,就為1;再進的是B元素,就為2...以此類推。類似的形式在實驗室內就幫助學長實現過,所以定義類個類並不難。但是,需要注意的是要在類中寫出比較方法,確定比較元素是誰。但是,我第一次實現的過程中,將這個計數的比較放在外部進行,這樣實現的話可以確定誰先進對誰先出隊。但是在實現輸出佇列內容的時候,會發現輸出的內容不是進隊的形式。而是中序的形式(問題3會解決此問題)。但是,我發現佇列的輸出形式可以每次刪除堆的根結點,用一個暫時變數存放內容,這樣的話就復刻了一份堆,利用了復刻的堆實現輸出而保持了原堆的內容。但是在實現這種情況會始終出現兩個堆被刪的一乾二淨,雖然會按照佇列的形式進行輸出,但是往後刪除就會有問題。經過改寫,嘗試把刪除元素存放到暫時變數中,在給它塞回去。這種方法雖然實現了不報錯,但是始終會輸出第一個元素,其他元素不輸出。我認為是因為我規定的存放數字是第一個就永遠第一個。通過侯澤洋同學的交流,發現我的程式碼都是複製貼上,沒用到繼承,如果用繼承的話就直接呼叫父類的方法就可以了,而且規定的標記數字也可以寫進方法裡面,不用自己規定,保持了佇列的完整性。但是,這樣的話我還是隻輸出第一個元素。後來感覺如歸的位置重新定義一下,就執行成功了,可能和第一次犯錯一樣就是在再次入隊的時候他的標記數字仍是之前的就會一直輸出,重新定義一下就會將之前的標記數字抹去換新。

    進行復刻導致兩個堆的元素都被刪除:


    實現佇列輸出(只是第一個元素的輸出):


    真正實現佇列輸出:

  • 問題3:如何改寫陣列實現堆的輸出形式?
  • 問題3的解決方案:這個問題是在課堂測試上發現的,但是我在ArrayHeap方法裡面並沒有找到toString方法,但是呼叫toString沒有問題。後來通過單步除錯跳到了ArrayBinary的toString。通過繼承會呼叫父類的方法,如過在子類中重寫方法會呼叫子類的方法。連結串列實現的堆也是如此。所以,針對toString方法我們可以有兩種修改方法,一是修改子類重寫,二是修改父類直接呼叫父類的。

程式碼託管

上週考試錯題總結

  • 第九章:
  • 錯題1: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
  • 錯誤解析:插入排序是一種反覆的把某個元素插入到之前已經排好序的列表中。

  • 錯題2:The best comparison sort in terms of order is:
    • A .O(1)
    • B .O(n)
    • C .O(log(n))
    • D .O(nlog(n))
  • 錯誤解析:根據各個排序方法的比較快速和歸併排序都是O(nlogn)的時間複雜度,執行效率高。

  • 第十章:
  • 錯題3:The Java Collections API provides two implementations of balanced binary search trees, TreeSet and TreeMap, both of which use a ___________tree implementation.
    • A .AVL
    • B .red/black
    • C .binary search
    • D .None of the above
  • 錯誤解析:通過實驗二的第六部分學習,知道兩個類都是紅黑樹。

  • 第十一章:
  • 錯題4:A minheap stores its largest element at the root of the binary tree, and both children of the root of a minheap are also minheaps.
    • A .True
    • B .Flase
  • 錯誤解析:最小堆將其最小元素存放到根處,不是最大元素,最大堆將其最大元素存放到根處。

  • 錯題5:Since a heap is a binary search tree, there is only one correct location for the insertion of a new node, and that is either the next open position from the left at level h or the first position on the left at level h+1 if level h is full.
    • A .True
    • B .Flase
  • 錯誤解析:堆是一個完全樹,不是二叉查詢樹。

  • 三章的錯題每次測試都差一點點滿分,而且在寫此部分的過程中發現每道題都是學過的邊邊角角之類的問題。暴漏出學習的不足,看書還是不夠仔細,針對邊界類的問題沒有仔細思考。

結對與互評

點評(王禹涵)

  • 部落格中值得學習的或問題:
    • 背景圖很好看,還是截圖內容看得不清晰,建議換個背景圖。
  • 程式碼中值得學習的或問題:
    • 程式碼圖片也是看的不清晰,下次嘗試用電腦做出的框圖來嘗試。
  • 基於評分標準,我給本部落格打分:8分。
    • 得分情況如下:
    • 正確使用Markdown語法(加1分)
    • 模板中的要素齊全(加1分)
    • 教材學習中的問題和解決過程, 三個問題加3分
    • 程式碼除錯中的問題和解決過程, 一個問題加1分
    • 感想,體會不假大空的加1分
    • 點評認真,能指出部落格和程式碼中的問題的加1分

點評(方藝雯)

  • 部落格中值得學習的或問題:
    • 圖片特別細緻,但是針對那一大串程式碼建議配一些講解,建議不要全放到程式碼註釋中。
  • 程式碼中值得學習的或問題:
    • 程式碼問題沒有看到。
  • 基於評分標準,我給本部落格打分:6分。
  • 得分情況如下:
    • 正確使用Markdown語法(加1分)
    • 教材學習中的問題和解決過程, 三個問題加3分
    • 程式碼除錯中的問題和解決過程, 無個問題加0分
    • 感想,體會不假大空的加1分
    • 點評認真,能指出部落格和程式碼中的問題的加1分

互評物件

感悟

第十二章的優先佇列與堆的內容並不是很難,和第十一章類似,都是在樹的基礎上新增附加條件,所以並不是很難,而且有很多示例程式碼可以學習,這樣的話更減少困難。

學習進度條

程式碼行數(新增/累積) 部落格量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 0/0 1/1 15/15
第二週 703/703 1/2 20/35
第三週 762/1465 1/3 20/55
第四周 2073/3538 1/4 40/95
第五週 981/4519 2/6 40/135
第六週 1088/5607 2/8 50/185
第七週 1203/6810 1/9 50/235
第八週 2264/9074 2/11 50/285

參考資料