堆排序,怎麼個堆法?

大家好,我是道哥。今天我們來聊重要的堆排序。堆排序在面試中是常考的內容,而且,堆也常用於處理各種海量數據面試題。

我們先看看究竟什麼是堆?以大頂堆爲例:

對於一棵完全二叉樹而言,當每個結點不小於其子結點時,便可稱之爲堆 (大頂堆),比如:

原始的待排序的數組爲:

30, 20, 40, 10, 0, 60, 80, 70

其對應的完全二叉樹爲:

接下來,我們來圖解堆排序,並用程序來實現堆排序。在這個過程中,希望大家感受到堆之美。

圖解堆排序

一. 構建堆

第 1 步:

如上圖,最後一個非葉子結點是 10,發現 10 比 70 小,所以 70 必須上浮,得到的結果爲:

第 2 步:

如上圖,倒數第二個非葉子結點爲 40,在 40,60,80 這三個數中,80 最大,所以 80 必須上浮,得到的結果如下:

第 3 步:

如上圖,倒數第三個非葉子結點爲 20,而 20 比 70 小,所以 70 必須上浮,20 下沉後,發現比下面的 10 還大,所以沒有必要沉底,得到的結果爲:

第 4 步:

如上圖,倒數第四個非葉子結點爲 30,在 30,70,80 中,80 最大,所以 80 要上浮,30 下沉。然而,30 比 60 和 40 都小,所以要繼續下沉,得到的結果是:

到此爲止,可以看到,一個大頂堆已經形成,可以看到,最大的 80 已經被選擇出來了。

二. 調整堆

我們把堆頂的最大值 80 調整到最後,保存下來,得到的結果是:

接下來的工作就是對上面紅框中的的 7 個結點進行調整,使之形成新的堆。

很顯然,根據之前調整的過程可知,兩個藍色框中的結點,已經分別成堆了,所以這次的調整就簡單多了,直接瞄準待調整的 10 即可。

之前已經把 8 個結點調整成堆,那麼調整上面紅色框中的 7 個結點成堆便不在話下。於是,這 7 個結點中最大的 70 被調到了堆頂,如下:

80 是最大的值,放在最後。堆頂的 70 是第二大的值,放在倒數第二的位置,所以跟 40 進行交換,得到的結果爲:

可見,通過 2 次從堆頂摘下最大元素,分別把 80 和 70 選出來了。接下來,用相同的方法,把 60 選出來,依此循環,最後得到的二叉樹爲:

終於,實現了排序,這就是所謂的堆排序,其平均時間複雜度爲 O(N*logN), 比冒泡排序好多啦。

堆排序實現

接下來,我們用代碼來實現堆排序,如下:

#include<iostream>
using namespace std;
void print(int a[], int n)
{
  int i;
  for(i = 0; i < n; i++)
  {
    cout << a[i] << " ";
  }
  cout << endl;
}
void heapAdjust(int a[], int low, int high)
{
  int pivotKey = a[low - 1];
  int i;
  for(i = 2 * low; i <= high; i *= 2) 
  {
    if(i < high && a[i - 1] < a[i])
    {
      i++; //i指向較大值
    }
    if(pivotKey >= a[i - 1])
    {
      break;
    }
    a[low - 1] = a[i - 1];
    low = i; 
  }
  a[low - 1] = pivotKey; 
}
void heapSort(int a[], int n)
{
  int i, tmp;
  for(i = n/2; i > 0; i--) 
  {
    heapAdjust(a, i, n);
    print(a, n);
  }
  for(i = n; i > 1; i--)
  {
    tmp = a[i -1];
    a[i - 1] = a[0];
    a[0] = tmp;
    heapAdjust(a, 1, i - 1); 
    print(a, n);
  }
}
int main()
{
  int a[] = {30, 20, 40, 10, 0, 60, 80, 70};
  int n = sizeof(a) / sizeof(a[0]);
  heapSort(a, n);
  print(a, n);
  return 0;
}

最終的排序結果如下:

0 10 20 30 40 60 70 80 

堆是一種重要的數據結構,堆排序也是非常重要的算法。在筆試面試中,經常考到堆的相關應用。

我們也會一步一個腳印,爭取每篇文章講清講透一件事,也希望大家閱讀後有所收穫,心情愉快。

你好,我是道哥,CSDN 前 30 名,曾混跡於 BAT 大廠。公衆號講解計算機基礎、網絡、數據結構、算法、C++、Java、Golang 等多方面的編程知識。歡迎點擊如下名片關注道哥,感謝點贊和在看支持哦。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/L2oxHKiQbj5OL2FK9eLe4w