堆排序演算法
優先佇列
堆的性質的維護,以下都以最大堆為例。
堆的維護的主要思想是「逐層下降」。舉例:某個結點i, 假設其左右子結點left(i),right(i)都已經是最大堆,那麼需要調節(或者說是調換)i, left(i), right(i)的值,並保證調換後的子樹繼續調換下去直到子樹繼續滿足堆的性質。首先,如果結點i的值大於左右子結點的值(即i的值是三個值中的最大值),則不需要調換,因為左右子結點均滿足堆的性質,同時結點i也滿足了結點值大於等於子結點的值,所以整棵樹就是最大堆(也有叫大頂堆)。其次,如果結點i的值不是三個中的最大值,不妨設左結點的值最大(即左結點的值比右結點以及i的值都大),則把i的值跟左結點的值調換,此時i, left, right滿足了父結點值大於等於左右子結點值的情況,那麼被調換後的左結點的值(變小了),以該結點為參考物件,它因為變小了,那麼它還滿足堆的要去嗎,所以需要繼續進行檢測和調換,即逐層的進行下去,直到原來的i值滑落到穩定的位置。時間複雜度是o(lgn)。以下是演算法**的具體驗證:
/*
最大堆的維持輸入引數是乙個陣列,和乙個小標i,
表示維持i到heap-size使該範圍的二叉樹為乙個最大堆。
注意乙個前提:結點i的左右子樹都已經是最大堆。
此處假設a的陣列長度size與heap-size一致。
知道堆的性質的維護方法以後,建堆的思路也就有了,就是從已經堆化的部分逐步往上呼叫最大堆化函式,使整個陣列逐漸全部滿足堆的要求。底層的每乙個單獨的葉結點是滿足堆的要求的,葉結點有a.length - a.length/2 個。所以從葉結點的父結點那一層開始建堆,這些點的下標範圍是:1– a.length/2 建堆的過程是從後往前每個點呼叫最大堆化函式,所以可能會有人認為複雜度是o(nlgn),這個結果雖然正確但不是漸近緊確的,經過證明可以得到建堆過程的乙個更緊確的界:o(n)。證明方法請參考演算法導論。一下是建堆過程的**示例:
給定陣列a,如要對陣列a進行排序輸出,則第一步是根據a數組建堆,此步時間複雜度為o(n), 建好堆的陣列a的最大元素一定在a[1](演算法描述裡陣列的首個位置下標為1,**裡陣列的首個位置下標為0,此處再做一次區分)。那麼我把a[1]與a[n]調換,則此時陣列裡的最大元素已經跑到陣列的最後位置,那麼陣列的前n-1個元素是什麼樣的情況,由於a[1]和a[n]互換,所以新的a[1]結點以及其左右子結點組成的********不一定滿足最大堆的性質了(父結點的值大於等於子結點的值),但是其左右子結點對應的樹,依然是滿足堆的性質的,所以對於a的前n-1個元素,只需要對a[1]結點呼叫維持堆化過程,整個n-1個元素就又變成了新的最大堆,其第乙個元素a[1]就是n-1個元素中的最大值,也即是陣列a中的次大值, 此時把a[1]與a[n-1]交換,則次大值放入了正確的位置,依次類推,直到位置2-n都放入了相應的值,則排序結束,時間複雜度o(nlgn)。以下是**示例:
略參考:演算法導論
堆和堆排序
堆是一種靈巧的 部分有序的資料結構,它尤其適合用來實現優先佇列。優先佇列是元素的乙個集合,其中每個元素都包含乙個被稱為元素優先順序的可排序屬性。優先佇列支援下面的操作 通過採用堆這種資料結構可以高效實現這些操作。下文分兩部分 第一部分介紹堆 第二部分講解堆排序。堆可以定義為一棵二叉樹,樹的節點中包含...
堆和堆排序
這個題大意是有乙個資料結構支援兩種操作a與 get操作,其中 a x表示插入 x.get i 表示返回結構中的第 i小的數.給你 a和get操作的順序和引數 現在要你對每個 get輸出值 題解 每次取第k小元素,k不斷更新。使用兩個堆,來完成。小頂堆負責選出最小的元素,大頂堆負責選出k個元素中最大的...
堆和堆排序
用陣列來儲存堆,下標為i的結點,父節點的編號為 i 1 2,子結點的編號為2 i 1,2 i 2。建立堆 每次插入乙個元素並調整堆的過程。插入乙個元素 插入到陣列最後,更新樹。刪除乙個元素 刪除發生在nums 0 將最後乙個元素調整到nums 0 處,更新樹。長度為len的陣列,最後乙個葉子節點的父...