20172301 《程式設計與資料結構》第八週學習總結
阿新 • • 發佈:2018-11-10
20172301 《程式設計與資料結構》第八週學習總結
教材學習內容總結
堆
- 堆:是具有兩個附加屬性的一棵二叉樹。
- 是一棵完全樹。
- 最小堆對每一結點,它小於或等於其左孩子和右孩子。反之,最大堆對每一個結點大於或等於它的左右孩子。
- ,最小堆將其最小元素儲存在該二叉樹的根處,且最小堆根結點的子樹同樣也是最小堆。
- addElement操作:將給定的元素新增到堆中的恰當位置,維持該堆的完全性屬性和有序屬性。
- 如果元素不是Comparable型別的,則會丟擲異常。這是為了讓元素可比較,可以維持堆的有序屬性。
而為了維護堆的完全性,就決定了堆插入結點時只有兩種情況:
1.h層的左邊下一個位置。
2.h+1層的第一個位置。(h層為滿)- 將新元素新增到堆的末尾後,考慮到有序屬性,將該元素與父結點進行比較,若小將它與父結點對換,直到大於父結點或者位於根結點處。
- removeMin操作:刪除堆的最小元素:刪除堆的最小元素並且返回。
- 最小元素位於根結點,刪除掉根結點,為了維持樹的完全性,要找一個元素來替代它,那麼只有一個能替換根的合法元素,且它是儲存在樹中最末一片葉子上的元素。最末的葉子是h層上最右邊的葉子。
- 考慮到有序屬性,對該堆重新排序。將新的根元素和其較小的孩子比較,如果孩子更小,那麼他們互換,直到該元素位於某個葉子中或者比他的兩個孩子都小。
- findMin操作:指向最小堆的最小元素。
- 返回存在根處的元素。
用連結串列實現堆
- 因為要求插入元素以後能夠向上遍歷,所以堆中的結點必須儲存指向雙親的指標。繼承
BinaryTreeNode類
,並且新增雙親指標和對應的set方法。 有一個例項資料
lastNode
作用是跟蹤記錄堆中的最後一個葉子,也就是指向末結點的引用。
- addElement操作:
- 在適當位置新增一個元素。
- 對堆進行重排序,以保持其有序屬性。
- 將
lastNode
指標重新設定為指向新的最末結點。 - 在最壞的情況下,確定要插入結點的雙親,需要從堆的右下結點往上遍歷到根,然後往下遍歷到堆的左下結點。時間複雜度為2 * logn。插入新結點,簡單賦值,時間複雜度為O(1)。如果需要重排序,最多需要比較logn次,因為路徑最長為logn。所以
addElement
- removeMin操作:
- 用儲存在最末結點處的元素替換儲存在根處的元素。
- 對堆進行重排序。
- 返回原來的根元素。
- 在最壞的情況下,替換結點,簡單賦值,時間複雜度為O(1),然後重排序,,因為路徑最長為logn,所以還是比較logn次。確定新的最末結點,從葉子到根的遍歷,再從根到另一個葉子的遍歷。所以
removeMin
操作的複雜度為2 * logn + logn + 1,為O(logn)。
- findMin操作
- 直接返回根元素,複雜度為O(1)。
用陣列實現堆
- 樹的根位於位置0處,對於每一結點n,n的左孩子將位於陣列的2n+1位置處,n的右孩子將位於陣列的2(n+1)位置處。
- addElement操作:
- 在恰當位置處新增新結點。
- 對堆進行重排序以維持其排序屬性。
- 將count值遞增1。
- 時間複雜度為 1 + log ,為 O(logn)。
- removeMin操作
- 用儲存在最末元素處的元素替換儲存在根處的元素。
- 對堆進行重排序。
- 返回初始的根元素,並將count值減1。
- 時間複雜度為 1 + log ,為 O(logn)。
- findMin操作
- 指向索引為0,時間複雜度為O(1)。
使用堆:優先順序佇列
- 遵循兩個排序規則:
- 具有更高優先順序的專案在先。
- 具有相同優先順序的專案使用先進先出方法來確定順序。
- 雖然最小堆根本就不是一個佇列,但是它卻提供了一個高效的優先順序佇列實現。
使用堆:堆排序
- 原理:根據堆的有序屬性,將列表的每一個元素新增到堆中,然後一次一個的把他們從根中刪除。
- 堆排序是一種選擇排序,整體主要由構建初始堆+交換堆頂元素和末尾元素並重建堆兩部分組成。其中構建初始堆經推導複雜度為O(n),在交換並重建堆的過程中,需交換n-1次,而重建堆的過程中,根據完全二叉樹的性質,[log2(n-1),log2(n-2)...1]逐步遞減,近似為nlogn。所以堆排序時間複雜度一般認為就是O(nlogn)。
- 堆排序時間複雜度O(nlogn)。
- 步驟:
- 步驟一:構造初始堆。將給定無序序列構造成一個堆(升序採用小頂堆,降序採用大頂堆)。
- 步驟二:將堆頂元素與末尾元素進行交換,使末尾元素最大。然後繼續調整堆,再將堆頂元素與末尾元素交換,得到第二大元素。如此反覆進行交換、重建、交換。
教材學習中的問題和解決過程
- 問題1:連結串列堆和陣列堆的優缺點。
- 問題1解決方案:
- 在之前章節的樹的實現中,我們大多數用的都是連結串列實現的。主要是因為,用陣列實現樹的時候,可能會因為沒有左右孩子而浪費了大量的空間。但是,在堆的實現中,因為考慮其向上遍歷的特殊操作,我們需要其雙親結點 。而又因為堆是一個完全樹,所以,不會存在大量浪費空間的情況 。所以,針對堆來說,陣列實現的效率更高。
根據書P268 和 P270,
因為陣列不需要確定新結點雙親的步驟,以及陣列不需要確定新的最末結點。所以,雖然他的時間複雜度和用連結串列實現時是一樣的,但是陣列實現的效率更高一些。
- 同樣,我們不必拘泥於一種結構,一種實現方式。平常編寫程式碼時也要注意其效率,程式碼的相關優化和美觀。不要只把實現和完成任務當成標準。這是我以後也應該注意的。
- 問題2:對於課上將的堆排序,還有課上的堆排序實踐沒有熟練掌握。重點歸納記憶一下。
- 問題2解決方案:
首先,要清楚堆排序的思想,堆排序是一種選擇排序 。如何將一個雜亂排序的堆重新構造成最大堆,它的主要思路就是
從上往下,將父節點與子節點以此比較。如果父節點最大則進行下一步迴圈,如果子節點更大,則將子節點與父節點位置互換,並進行下一步迴圈。注意父節點要與兩個子節點都進行比較。
我們第一步就應該明白,如何將一個無序列表構建成最大堆。
從最後一個非葉節點開始調整。
如圖,這裡的最後一個非葉子結點是結點4,那麼我們就從這裡進行調整。
每次調整都是從父節點、左孩子節點、右孩子節點三者中選擇最大者跟父節點進行交換(交換之後可能造成被交換的孩子節點不滿足堆的性質,因此每次交換之後要重新對被交換的孩子節點進行調整)。
- 如上圖,這裡從結點2開始做調整。左孩子為結點4,右孩子為結點5,將其與父結點做比較,發現左孩子比父結點更大。因此將它們做交換,設結點4為最大的結點,並繼續以結點4開始做下一步運算。
- 當構建好一個堆之後,我們開始進行排序。將堆頂元素與末尾元素進行交換,使末尾元素最大。然後重新調整堆,一直到最後整個堆都不存在了,那麼陣列就是有序的。
程式碼除錯中的問題和解決過程
- 問題1:在實現PP12.1的時候,發現使用remove操作之後,雖然刪除了第一個進入的元素,但是又添加了一個元素。
如圖,發現,我插入佇列的依次是,2,10,3。然後中序遍歷就是10,2,3。這是沒有問題的,但是刪除之後,雖然2刪除掉了,但是多了一個3。
- 問題1解決方案:
- 這個問題可能是因為我
dequeue()
方法,呼叫了之前父類ArrayHeap
類中removeMin()
方法所引起的。 - 所以就不得不說一下我PP12.1的實現思路。實際上,佇列和優先順序佇列一定程度上是一樣的。只是
CompareTo
方法的判定條件不同。佇列只需要將進入堆的順序記錄下來就可以然後比較即可。那麼這裡的呼叫的removeMin()
方法實際上就是刪除佇列中順序最低的,也就是第一個進來的元素。
public T removeMin() throws EmptyCollectionException { if (isEmpty()) throw new EmptyCollectionException("ArrayHeap"); T minElement = tree[0]; tree[0] = tree[count-1]; heapifyRemove(); count--; modCount--; return minElement; }
- 程式碼如上所示,可以發現,在把陣列的最後一個賦給第一個,重排序之後呢,並沒有對其進行清空操作。所以,存在會有兩個3的出現。那麼我只需要在之後新增一串
就可以解決問題了。tree[count] =null;
- 這個問題可能是因為我
程式碼託管
上週考試錯題總結
- 在二叉查詢樹刪除結點時,如果他有孩子,那麼讓他的孩子代替他。應該是升級而不是降級。
結對及互評
點評過的同學部落格和程式碼
- 上週部落格互評情況
其他
堆是基於二叉樹的。這周看程式碼的效率並不是很高,對於程式碼的理解也不是很深入。歸結於學習積極性不高。凡事不要鑽牛角尖,過去的就過去了,一切都會好起來。不要給自己額外的壓力和負擔。
看過別的同學優秀的部落格以後發現自己不能懈怠。比如很多同學都聯絡到了上學期的堆疊記憶體時的學習內容。學習應該有體系,不能夠東拼西揍,撿西瓜丟芝麻。還是要踏實下來,戒驕戒躁。
學習進度條
程式碼行數(新增/累積) | 部落格量(新增/累積) | 學習時間(新增/累積) | 重要成長 | |
---|---|---|---|---|
目標 | 5000行 | 30篇 | 400小時 | |
第一週 | 0/0 | 1/1 | 10/10 | |
第二週 | 610/610 | 1/2 | 20/30 | |
第三週 | 593/1230 | 1/3 | 18/48 | |
第四周 | 2011/3241 | 2/5 | 30/78 | |
第五週 | 956/4197 | 1/6 | 22/100 | |
第六週 | 2294/6491 | 2/8 | 20/120 | |
第七週 | 914/7405 | 1/9 | 20/140 | |
第八週 | 2366/9771 | 2/11 | 22/162 |