圖解堆排序,帶你徹底瞭解清楚!

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"寫在前面:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"大家好,我是時光。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"今天給大家帶來的是排序算法中的堆排序,這種排序跟二叉樹相關。我採用圖解方式講解,爭取寫透徹。話不多說,開始!","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"思維導圖:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/36/3672a13f483e65c17c9b97f926f7cc26.png","alt":"堆排序導圖","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"堆排序導圖","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"1,堆排序概念","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"堆排序","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"(Heapsort)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"是指利用堆這種數據結構所設計的一種排序算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"相關概念:","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"1.1,二叉樹","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/51/51cd55cc60aab46dc095975d1b497b04.png","alt":"二叉樹","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"二叉樹","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"特徵:每個節點最多隻有2個子節點(不存在度大於2的節點)","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"1.2,滿二叉樹","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b7/b7a3823ab5d08f86b05970848f3c7ea3.png","alt":"滿二叉樹","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"滿二叉樹","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"滿二叉樹:葉子節點全部都在最底層;除葉子節點外,每個節點都有左右孩子;","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"1.3,完全二叉樹","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4c/4c4f3f8f299314ff1bea892fe76bad7f.png","alt":"完全二叉樹","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"完全二叉樹","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"完全二叉樹:葉子節點全部都在最底的兩層;最後一層只缺少右邊的葉子節點,左邊一定有葉子節點;除了最後一層,其他層的節點個數均達到最大值;","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"1.4,最大堆和最小堆","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最大堆:堆頂是整個堆中","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"最大元素","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最小堆:堆頂是整個堆中","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"最小元素","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"2,算法思路","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們先搞清楚這個堆排序思想,先把邏輯搞清楚,不着急寫代碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們首先有一個無序數組,比方說","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"int[] arr={4,2,8,0,5,7,1,3,9};\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然用到堆,肯定先要將數組構建成二叉堆。需要對數組從小到大排序,則構建成最大堆;需要對數組從小到大排序,則構建成最小堆。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"2.1,第一個步驟,初始化堆","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們先來看看數組是如何存儲二叉樹的","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2b/2b3992fe22f3ac7ed0b9288b6f9e3474.png","alt":"","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"注意:這裏考慮的當前節點,必須是完全二叉樹的","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"非葉子節點","attrs":{}},{"type":"text","text":"。並且節點的左孩子和右孩子必須小於數組長度,所以後面需要添加限制條件。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"看到上圖中的公式,我們明白了數組是可以存儲完全二叉樹,同時保留節點之間的關係。以上述數組爲例","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e0/e0aeba5870f1c54f466cb3310f0ee62d.png","alt":"數組存儲完全二叉樹","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"數組存儲完全二叉樹","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"那麼存儲好之後,如何將二叉樹構建成二叉堆呢?繼續往下看","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/24/2427bf59c00d437254806711a993c34f.png","alt":"完全二叉樹","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"完全二叉樹","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"以這個圖爲例,我們以最大堆舉例,若要構建二叉堆,則A要比B和C都大,B要比D和E都大,C要比F和G都大。也就是說父節點要比子節點都大才行。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3c/3c5f5c29c37421afb624bc0dd6297610.png","alt":"記錄數組下標的二叉樹","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"記錄數組下標的二叉樹","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"在這個圖中,明顯不滿足最大堆的要求。我們先拿序號爲3,7,8的三個節點來研究,i=3的節點比i=7和i=8的節點都小,所以需要交換位置。注意此圖是從0開始,也就是模擬數組下標從0開始。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"怎麼交換呢?很簡單。我們看節點0,設爲當前節點","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",那麼它的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lchild=index*2+1","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",它的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"rchild=index*2+2","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":";注意下標從0開始。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//初始化堆\npublic static void HeapAdjust(int[] arr,int index,int len){\n        //先保存當前節點的下標\n        int max = index;\n        //保存左右孩子數組下標\n        int lchild = index*2+1;\n        int rchild = index*2+2;\n        //開始調整,左右孩子下標必須小於len,也就是確保數組不會越界\n        if(lchild arr[max]){\n            max=lchild;\n        }\n        if(rchild arr[max]){\n            max=rchild;\n        }\n        //若發生了交換,則max肯定不等於index了。此時max節點值需要與index節點值交換,上面交換索引值,這裏交換節點值\n        if(max!=index){\n            int temp=arr[index];\n            arr[index]=arr[max];\n            arr[max]=temp;\n            //交換完之後需要再次對max進行調整,因爲此時max有可能不滿足最大堆\n            HeapAdjust(arr,max,len);\n        }\n    }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"上面代碼很好理解,中間的兩個if語句就是交換節點索引值,只要有一個孩子節點大於","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",就需要進行交換。若父節點","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"比兩個孩子節點都大,那麼就不需要交換了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"最後面的if語句是交換節點值,我們知道,只要","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"與","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lchild","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"rchild","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"有交換,則","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"一定不等於","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"max","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"了!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"那爲什麼最後的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"if","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"語句裏面還要加上一個遞歸寫法呢?我們舉個例子就明白了,還是以上述數組爲例,先看看一輪交換之後的樣子:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"第一次交換,0與9交換,此時Index=9;","attrs":{}}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/be/be2ed9d975beb97adff3774147fe17c6.png","alt":"0與9交換","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"0與9交換","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"第二次交換,8已經比7和1都大了,此時不需要交換;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"第三次交換,2與9交換,此時","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"=9;","attrs":{}}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b8/b840da4fa7151a27802e47909d399666.png","alt":"2與9交換","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"2與9交換","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"第四次交換,4與9交換,此時","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"=9,第一輪交換到此結束。","attrs":{}}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f6/f6c23a6d4bd4af6a34aaa1004d17009f.png","alt":"4與9交換","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"4與9交換","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"一輪結束後,有小夥伴兒已經發現問題了,雖然9成功的成爲最大堆的堆頂元素,但是下面的其他元素並不滿足最大堆的要求,比如說左下角的元素2,元素3,元素0等這種二叉樹就不滿足,元素4,元素2,元素5也不滿足。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"因此在交換節點值這個步驟裏,我們需要進行遞歸操作,交換完之後再次對","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"進行堆調整:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//若發生了交換,則max肯定不等於index了。此時max節點值需要與index節點值交換,上面交換索引值,這裏交換節點值\nif(max!=index){\n    int temp=arr[index];\n    arr[index]=arr[max];\n    arr[max]=temp;\n    //交換完之後需要再次對max進行調整,因爲此時max有可能不滿足最大堆\n    HeapAdjust(arr,max,len);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"堆排序的測試:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//堆排序\n    public static int[] HeapSort(int[] arr){\n        int len=arr.length;\n        /**\n         * 第一步,初始化堆,最大堆,從小到大\n         * i從完全二叉樹的第一個非葉子節點開始,也就是len/2-1開始(數組下標從0開始)\n         */\n        for(int i=len/2-1;i>=0;i--){\n            HeapAdjust(arr,i,len);\n        }\n        //打印堆頂元素,應該爲最大元素9\n        System.out.println(arr[0]);\n        return arr;\n    }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"上述代碼就是從完全二叉樹的第一個非葉子節點開始調換,還順便打印堆頂元素,此時應爲9;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"至此,第一個步驟,初始化堆完成,最後的結果應該爲下圖:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f8/f8be4cb384050d77f18667e5d1d4796e.png","alt":"初始化堆結束","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"初始化堆結束","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"2.2,第二個步驟,堆排序","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"堆排序的過程就是將堆頂元素(最大值或者最小值)與二叉堆的最末尾葉子節點進行調換,不停的調換,直到二叉堆的順序變成從小到大或者從大到小,也就實現了我們的目的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們這裏以最大堆的堆頂元素(最大元素)爲例,最後調換的結果就是從小到大排序的結果。","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"第一次交換,我們直接將元素9與元素0交換,此時堆頂元素爲0,設當前節點","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"=0;","attrs":{}}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a2/a223543ae4c743741760df8bb55e7470.png","alt":"0與9交換","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"0與9交換","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"這時,我們需要將剩下的元素(排除元素9)進行堆排序,直到下面這個結果:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0a/0a9b0d3f8272d85eba25685cbcf8e746.png","alt":"除元素9以外剩下的元素排序","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"除元素9以外剩下的元素排序","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"代碼:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * 第二步,交換堆頂(最大)元素和二叉堆的最後一個葉子節點元素。目的是交換元素\n * i從二叉堆的最後一個葉子節點元素開始,也就是len-1開始(數組下標從0開始)\n */\nfor(int i=len-1;i>=0;i--){\n    int temp=arr[i];\n    arr[i]=arr[0];\n    arr[0]=temp;\n    //交換完之後需要重新調整二叉堆,從頭開始調整,此時Index=0\n    HeapAdjust(arr,0,i);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"注意:這裏有個小細節問題,前面我們寫的初始化堆方法有三個參數,分別是數組","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"arr","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",當前節點","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"以及數組長度","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"len","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",如下:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"HeapAdjust(int[] arr,int index,int len)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"那麼,爲何不直接傳入兩個參數即可,數組長度直接用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"arr.length","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"表示不就行了嗎?初始化堆的時候是可以,但是後面在交換堆頂元素和末尾的葉子節點時,在對剩下的元素進行排序時,此時的數組長度可不是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"arr.length","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"了,應該是變化的參數","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"i","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",因爲交換之後的元素(比如9)就不計入堆中排序了,所以需要3個參數。","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#616161","name":"user"}}],"text":"我們進行第二次交換,我們直接將元素8與元素2交換,此時堆頂元素爲2,設當前節點","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"index","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"=2;","attrs":{}}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e5/e51aa1aab63f70c8a589aecb087be57f.png","alt":"8與2交換","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"8與2交換","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"這時,我們需要將剩下的元素(排除元素9和8)進行堆排序,直到下面這個結果:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7f/7f7a8d2a714a44cbcf6f421f00b75470.png","alt":"除元素9和8以外剩下的元素排序","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"除元素9和8以外剩下的元素排序","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"到這個時候,我們再重複上述步驟,不斷調換堆頂和最末尾的節點元素即可,再不斷地對剩下的元素進行排序,最後就能得到從小到大排序好的堆了,如下圖所示,這就是我們想要的結果:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fd/fd8fbbbbc0bfbaba6d6b65cbb8b2b560.png","alt":"最終排序結果:從小到大","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"最終排序結果:從小到大","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"3,完整代碼","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.Arrays;\n\npublic class Head_Sort {\n    public static void main(String[] args) {\n        int[] arr={4,2,8,0,5,7,1,3,9};\n        System.out.println(\"排序前:\"+Arrays.toString(arr));\n        System.out.println(\"排序後:\"+Arrays.toString(HeapSort(arr)));\n    }\n\n    //堆排序\n    public static int[] HeapSort(int[] arr){\n        int len=arr.length;\n        /**\n         * 第一步,初始化堆,最大堆,從小到大。目的是對元素排序\n         * i從完全二叉樹的第一個非葉子節點開始,也就是len/2-1開始(數組下標從0開始)\n         */\n        for(int i=len/2-1;i>=0;i--){\n            HeapAdjust(arr,i,len);\n        }\n        /**\n         * 第二步,交換堆頂(最大)元素和二叉堆的最後一個葉子節點元素。目的是交換元素\n         * i從二叉堆的最後一個葉子節點元素開始,也就是len-1開始(數組下標從0開始)\n         */\n        for(int i=len-1;i>=0;i--){\n            int temp=arr[i];\n            arr[i]=arr[0];\n            arr[0]=temp;\n            //交換完之後需要重新調整二叉堆,從頭開始調整,此時Index=0\n            HeapAdjust(arr,0,i);\n        }\n        return arr;\n    }\n\n    /**\n     *初始化堆\n     * @param arr 待調整的二叉樹(數組)\n     * @param index 待調整的節點下標,二叉樹的第一個非葉子節點\n     * @param len 待調整的數組長度\n     */\n    public static void HeapAdjust(int[] arr,int index,int len){\n        //先保存當前節點的下標\n        int max = index;\n        //保存左右孩子數組下標\n        int lchild = index*2+1;\n        int rchild = index*2+2;\n        //開始調整,左右孩子下標必須小於len,也就是確保index必須是非葉子節點\n        if(lchild arr[max]){\n            max=lchild;\n        }\n        if(rchild arr[max]){\n            max=rchild;\n        }\n        //若發生了交換,則max肯定不等於index了。此時max節點值需要與index節點值交換,上面交換索引值,這裏交換節點值\n        if(max!=index){\n            int temp=arr[index];\n            arr[index]=arr[max];\n            arr[max]=temp;\n            //交換完之後需要再次對max進行調整,因爲此時max有可能不滿足最大堆\n            HeapAdjust(arr,max,len);\n        }\n    }\n}\n\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"運行結果:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/03/03806df817263a5c283f7a22931001ab.png","alt":"運行結果","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#595959","name":"user"}}],"text":"運行結果","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"4,算法分析","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"4.1,時間複雜度","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"建堆的時候初始化堆過程","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"(HeapAdjust)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":"是堆排序的關鍵,時間複雜度爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"O(log n)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",下面看堆排序的兩個過程;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一步,初始化堆,這一步時間複雜度是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"O(n)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":";","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二步,交換堆頂元素過程,需要用到n-1次循環,且每一次都要用到","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"(HeapAdjust)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",所以時間複雜度爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"((n-1)*log n)~O(nlog n)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":";","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終時間複雜度:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"O(n)+O(nlog n)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",後者複雜度高於前者,所以","attrs":{}},{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"堆排序的時間複雜度爲O(nlog n)","attrs":{}},{"type":"text","text":";","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"4.2,空間複雜度","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"空間複雜度是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"O(1)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#9654B5","name":"user"}}],"attrs":{}},{"type":"text","text":",因爲沒有用到額外開闢的集合空間。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}}],"text":"4.3,算法穩定性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#773098","name":"user"}},{"type":"strong","attrs":{}}],"text":"堆排序是不穩定的","attrs":{}},{"type":"text","text":",比方說二叉樹[6a,8,13,6b],(這裏的6a和6b數值上都是6,只不過爲了區別6所以這樣)經過堆初始化後以及排序過後就變成[6b,6a,8,13];可見堆排序是不穩定的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微信搜索公衆號《","attrs":{}},{"type":"text","marks":[{"type":"size","attrs":{"size":12}},{"type":"color","attrs":{"color":"#FF7021","name":"orange"}}],"text":"程序員的時光","attrs":{}},{"type":"text","text":"》","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"好了,今天就先分享到這裏了,下期繼續給大家帶來 排序算法面試內容!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更多幹貨、優質文章,歡迎關注我的原創技術公衆號~","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章