1. 程式人生 > >數據結構學習筆記-排序/隊/棧/鏈/堆/查找樹/紅黑樹

數據結構學習筆記-排序/隊/棧/鏈/堆/查找樹/紅黑樹

算法 數據結構

排序

插入排序:每次從剩余數據中選取一個最小的,插入已經排序完成的序列中

合並排序:將數據分成左右兩組分別排序,然後合並,對每組數據的排序遞歸處理。

冒泡排序:重復交換兩個相鄰元素,從a[1]開始向a[0]方向冒泡,然後a[2]...a[i]無法繼續往前擠的時候說明前面的更小了,而且越往前越小(擠得越往前)

堆排序:構造最大堆,每次取走根結點(自然是最大的),再調用MAX-HEAPIFY算法(見後文的堆)恢復最大堆的性質,重復取走根結點

快速排序(A[r]-A[n]進行排序):

1.序列中選取一個元素作為主元並與A[n]交換,或者直接把A[n]作為主元

2.序列劃分為左中右三部分,主元在中間。left中任何元素都

<=主元,right中任何元素都>主元。

具體劃分方式為:將序列的A[r]-A[n-1]部分劃分為三個依次相連的區域:left,right,未劃分,在未劃分的區域遍歷,如果某元素比主元大不作處理,如果比主元小,則將此元素與right區的第一個元素交換,並將新的right區的第一個元素劃給left區。遍歷完畢夠將A[n]right區的第一個元素交換。

([left][right][未劃分][主元],也就是把未劃分區域的元素不斷移動到left區或保持不動變成right區的一部分)

3.劃分好的左右兩部分分別排序,這樣遞歸下去,直到某一部分的元素數量少於等於三個可以直接進行排序。

隊棧鏈堆:

:先進後出的結構,push時棧頂升高,元素放入新棧頂;pop時取出棧頂元素,棧頂降低。

(循環)隊列:先進先出的結構,top指向隊首的位置,tail指向新元素要加入的位置(也就是隊尾的後一個位置)

enqueue時元素放入tail,tail循環後移;dequeue時取出top元素,top循環後移。

隊列的容量比數組容量少一。(否則當toptail相等時無法判斷隊列是空還是滿)

鏈表:前一個元素指向後一個元素(後一個也可能指向前一個),經典操作插入,刪除,搜索,可以在頭元素之前尾元素之後加入哨兵元素以簡化邊界判斷。

最大堆(最小堆同理):一顆完全二叉樹,而且每個結點都是所在子樹中的最大結點

最大堆MAX-HEAPIFY算法

:當最大堆的根結點的值發生了變化,可能不再是整個樹中最大的結點了,使用此算法可以維持最大堆的性質(每次和左右結點中最大的那個交換,遞歸下去,最終移動到合適的位置)

最大堆建堆(自下而上MAX-HEAPIFY):從第max/2個結點(最後一個元素是此元素的孩子結點,此元素再往後的點都沒孩子,自然都是最大堆),從此結點依次往前調用MAX-HEAPIFY算法,可以保證從下到上的每個子樹都是最大堆,那麽整體也是最大堆。

用最大堆實現最大優先級隊列:

1.取最大權重元素:A.First

2.移除最大權重元素:取出A.First,然後把A.Last移動到A.First並對新的A.First執行向下遞歸的MAX-HEAPIFY操作以維持最大堆性質。

3.插入元素:在最後面新增元素(成為新的A.Last),對A.Last向著父結點方向遞歸交換上升到適合的位置。

4.增加A[i]的權重:A[I]向著父結點方向遞歸交換上升到適合的位置。

二叉查找樹:

每個結點的KEY,都比其左子樹的任意結點的KEY大,比其右子樹的任意結點的KEY小。因此通過中序遍歷可以從小到大順序輸出(中序遍歷:先遍歷左子樹,再遍歷父結點,最後遍歷右子樹)

查找:從父到子從上到下,想找更小的數往左拐(如果要找的數小於根結點,則一定在左子樹中)。直到匹配,或者找到了NIL

最大KEY值元素:順著右子樹的右子樹的右子樹……找到底,因為右子樹總是比較大。

最小KEY值元素:同理,順著左子樹找到底。

後繼(中序遍歷中下一個遍歷到的結點):

1.如果結點有右子樹,則後繼是右子樹RT的左下角(左中右,下一個遍歷到的一定是RT的左子樹的左子樹的左子樹的左子樹。。。)

2.如果結點(設為c)不含有右子樹,則一路向上找祖先,直到找到某個結點s,使得c在這個結點的左子樹中(也就是說s的左孩子也是c的祖先,或者就是c),此時c的左子樹剛遍歷完畢即將遍歷s,自然s就是c的後繼了

前驅(和後繼問題對稱)

1.如果含有左子樹,則一定是左子樹的最大結點

2.如果沒有左子樹,則F所在的某顆右子樹馬上要被遍歷了,找前驅順著父節點往上找就是(直到找到某個結點S,使得S的右孩子也是F的祖先)

插入

像查找一樣從上到下根據和父結點大小的對比,選擇左轉還是右轉,直到落到合適的空位上。

刪除:

1.如果F沒有孩子結點,直接刪,什麽都不影響

2.如果F有一個孩子結點,刪除該結點,並且將它的孩子節點(C)連接到父結點(S)上

(這樣並不會改變S的左子樹所有結點比S小,右子樹所有結點比S大的局面)

3.如果F有兩個孩子結點,則把F的後繼刪除,然後用後繼替換F(後繼是右子樹左下角,最多只有一個右孩子,用12的方式刪了無影響;由於F和其後繼大小相鄰,替換後中序遍歷依然從小到大,說明沒有破壞查找樹的性質)

紅黑樹:

根節點和最底下無數據的葉子結點(Nil結點)為黑色,任意父子結點不能同時為紅(這樣可以保證每個路徑紅的數量不大於黑),任意路徑的黑結點數量相同(配合前面的性質,保證了最長的路徑最多不超過最短路徑的二倍,一種平衡策略)。插入刪除操作都是 0(lnN), 且最多旋轉三次

旋轉:

分左旋和右旋。旋轉改變某個結點和它的左孩子或右孩子的關系。因為改變上下的同時也改變了左右,因此不影響左<<右的查找樹性質。一定的旋轉和變色操作可以恢復紅黑樹的性質。

左旋:結點F繞著右孩子R逆時針旋轉90度,結果F變成了R的左孩子,R原來的左孩子分給了F的右邊,F原來的父結點被R連上了。左旋導致了右孩子的右側分支變短了。

右旋:結點F繞著左孩子L順時針旋轉90度,指針變更和左旋同理,導致左孩子的左分支變短了。

紅黑樹的插入:(插入的位置如同普通查找樹,每次只插入紅的,這樣只破壞紅色不相鄰的性質):

如果紅黑樹是空的:放入根結點的位置,變成黑色

如果父結點是黑的。不破壞性質。

之後以新結點的父結點是爺爺結點的左孩子為例;右孩子的情形與此對稱:

1.父結點和叔父結點都是紅的:

讓父結點和叔父結點變黑,祖父變紅,這樣解決了父子同紅的問題。如果恰好曾祖父為紅,則遞歸向上解決問題。

2.父紅,叔父黑,新結點是父結點的右孩子。則左旋父結點,這樣父子左右關系顛倒,變成情況3

3.父紅,叔父黑,新節點是父結點的左孩子。則右旋祖父結點(按照預設,父結點是祖父結點的左孩子)。此時父子雙紅的左分支高度降低,原來的父節點位置變低,跑到右側。此時再交換原祖父結點和父結點的顏色,就左右均衡了。

刪除

如果紅黑樹只有一個結點,直接刪除;

如果刪除的結點有兩個孩子,則實際刪除的是其後繼,變成沒有或者一個孩子的情況。

如果有一個孩子,則必定是黑結點紅孩子,直接用孩子結點替代父結點並且把顏色變黑即可。

如果結點沒有孩子且結點為紅色,直接刪除即可。

因此唯一需要討論的就是要刪除的結點為黑色而且沒有孩子結點。

包含多種情況,使用變色旋轉等技巧可以使樹平衡。看了好久好久也沒整理下來,不繼續浪費時間了


數據結構學習筆記-排序/隊/棧/鏈/堆/查找樹/紅黑樹