圖解堆排序演算法

小K演算法發表於2021-04-28

文章首發於微信公眾號:小K演算法,關注第一時間獲取更新資訊

1 演進

結點和邊,構成一個

圖解堆排序演算法

不含環的連通圖,便成了一棵。每個結點擁有的子結點數稱為結點的

圖解堆排序演算法

多棵樹便構成了一個森林

圖解堆排序演算法

結點的度最大為2的樹便是二叉樹;最大度為N的是N叉樹,或多叉樹

圖解堆排序演算法

除葉子結點,每個結點的度都為2,稱為滿二叉樹
除去最後一層之後的子樹為滿二叉樹,且最後一層結點依次從左到右分佈,則稱為完全二叉樹

圖解堆排序演算法

如果在完全二叉樹上再加一個限制條件:如結點都大於等於其子結點,或者小於等於其子結點,則稱為
每個結點都大於等於其子結點,稱為大根堆
每個結點都小於等於其子結點,稱為小根堆

圖解堆排序演算法

2 堆儲存

2.1 順序儲存:陣列

用陣列儲存,將一個線性陣列對映成一棵完全二叉樹,父結點為i,則左兒子為2i+1,右兒子為2i+2。

圖解堆排序演算法

程式碼如下

int heap[10];

2.2 鏈式儲存:連結串列

定義一個結點的結構體,兩個指標分別指向左兒子和右兒子。

圖解堆排序演算法

程式碼如下

struct Node {
    int value;
    Node *lson, *rson;
};
Node *heap;

以下思想都以大根堆舉例。

3 堆調整

3.1 向上調整

子結點與父結點的下標關係如下:

圖解堆排序演算法

用一個指標指向待調整的結點:

  • 先比較是否大於父結點,如果大於就進行交換,並將指標上移到父結點

直到指向根結點或者當前結點小於等於父結點。

圖解堆排序演算法

程式碼實現

//將heap[k]向上調整
int heapUp(int *heap, int k) {
    int parent, son, x;
    x = heap[k];
    son = k;
    parent = (son - 1) / 2;
    while (son > 0) {
        //如果父結點大於等於heap[k]則退出,否則將父結點下移
        if (heap[parent] >= x)
            break;
        heap[son] = heap[parent];
        son = parent;
        parent = (son - 1) / 2;
    }
    heap[son] = x;
    return 0;
}

3.2 向下調整

父結點與子結點的下標關係如下:

圖解堆排序演算法

用一個指標指向待調整的結點:

  • 先比較兩個子結點哪個更大,取出更大的子結點
  • 再比較更大的子結點是否大於父結點,如果大於就進行交換,並將指標下移

直到指向葉子結點或者當前結點大於兩個子結點。

圖解堆排序演算法

程式碼實現

//將heap[k]向下調整
int heapDown(int *heap, int k, int n) {
    int parent, son, x;
    x = heap[k];
    parent = k;
    son = 2 * k + 1;    //左孩子結點
    while (son <= n) {
        //比較左右兒子,選擇較大的一個
        if (son + 1 <= n && heap[son + 1] > heap[son])
            son++;    //使son指向左右孩子中較大的結點。
        //如果兒子結點中較大的都小於等於待調整結點則退出,否則將子結點上移
        if (heap[son] <= x)
            break;
        heap[parent] = heap[son];
        parent = son;
        son = 2 * parent + 1;
    }
    heap[parent] = x;
    return 0;
}

4 增減元素

4.1 push

從堆尾插入元素,再對該元素進行向上調整直到滿足堆性質。

圖解堆排序演算法

4.2 pop

將堆頂彈出,用堆尾的元素置換,再對堆頂的元素進行向下調整。

圖解堆排序演算法

5 構建堆

5.1 插入構建

依次向堆尾插入元素,並對該元素進行向上調整,直到滿足堆性質。

圖解堆排序演算法

時間複雜度:
插入一個元素要調整的高度為logi,所以插入n個元素的總次數為log1+log2+...+logn=log(n!)。

根據斯特林公式,有如下證明,所以複雜度O(nlogn)。

圖解堆排序演算法

5.2 調整構建

待調整的陣列,可以直接看成是一棵完全二叉樹。

圖解堆排序演算法

從(n-1)/2位置開始,將每個元素進行向下調整,直到根結點。對於每一個待調整的當前結點,下面的子樹都已經滿足堆性質,所以調整完所有結點便成了堆。

圖解堆排序演算法

時間複雜度:
倒數第二層有2^(h-2)個結點,調整高度為1,依次類推,第一層有1個結點,調整高度為h-1,整體加起來的複雜度為O(n)。

圖解堆排序演算法

程式碼實現

void buildHeap(int *heap, int n) {

    for (int i = (n - 1) / 2; i >= 0; --i) {
        heapDown(heap, i, n);
    }
}

6 排序

一個已經調整完成的大根堆。

圖解堆排序演算法

核心思想:

  • 將堆頂與堆尾的元素置換
  • 整體元素長度減1
  • 對堆頂元素進行向下調整

重複以上過程直到整體元素為1,這時就變成了一個升序排列的陣列。

模擬過程:
Step 1

圖解堆排序演算法

Step 2

圖解堆排序演算法

7總結

堆排的複雜度為nlogn,應用場景很廣泛,這篇文章主要講清楚堆相關的操作,具體的應用和建模以後會再專門寫文章講解。

如果喜歡小K的文章,請點個關注,分享給更多的人,小K將持續更新,謝謝啦!


掃描下方二維碼關注公眾號:小K演算法,第一時間獲取更新資訊!

圖解堆排序演算法

相關文章