【每日算法】堆排序

1、堆:堆數據結構是一種數組對象。

對於表示堆的數組arr[0…n-1],我們以arr[0]爲根,給定某個節點下標i,令其父節點和左右後代節點的下標爲:

parent(i) = (i-1)/2;

left(i) = 2*i+1;

right(i) = 2*i+2;

這裏寫圖片描述

堆分爲最大堆和最小堆,上面就是最大堆,特點就是:除根節點以外的每個節點i,都有arr[ parent(i) ] >= arr[i]。最小堆的特點則是:除根節點以外的每個節點i,都有arr[ parent(i) ] <= arr[i]。

堆排序一般使用最大堆,最大堆中的最大元素位於根節點。

因爲具有n個元素的堆是基於一顆完全二叉樹的,所以其高度爲O(log n)。

2、如何保持堆中的性質:

首先,我們假定以節點i的左右兒子爲根的兩棵二叉樹都是最大堆,而以節點i爲根的二叉樹可能不是最大堆,則調整的過程如下:

  1. 從元素arr[i], arr[left(i)], arr[right(i)]中找出最大的元素,將下標存在largest中;
  2. 如果arr[i]是最大的,說明以節點i爲根的二叉樹是最大堆,無須調整,程序結束;否則,交換arr[i]和arr[largest],於是arr[i], arr[left(i)], arr[right(i)]三者滿足了最大堆的性質,但是交換後,下標爲largest的節點存放arr[i]的值,以該節點爲根的子樹又可能違反最大堆的性質,因此需要對該子樹遞歸調用本調整過程
代碼:
//arr[0...size-1]
void maxHeapify(int arr[], int i, int size)
{
    int l = left(i);
    int r = right(i);
    int largest = i;
    if (l < size && arr[l] > arr[largest])
        largest = l;
    if (r < size && arr[r] > arr[largest])
        largest = r;
    if (largest != i)
    {
        exchange(arr[i], arr[largest]);
        maxHeapify(arr, largest, size);
    }
}


3、建堆:

上面保持堆的性質是一個鋪墊,它也是堆算法中的核心部分,後面我們將利用它完成建堆和堆排序。

我們先看看如何使用maxHeapify()來將一個數組arr[0…size-1]變成一個最大堆。

對於每一片樹葉,我們都可以看作是一個只含一個元素的堆。於是對於葉子節點的父親節點(左右子樹都是最大堆),我們可以調用maxHeapify()來進行調整。調整之後,我們得到更大的堆,對於這些堆的父節點,我們又可以調用maxHeapify()來進行調整。

爲保證maxHeapify()的調用前提,我們只需從最下面的非葉子節點開始調整,一直到根節點,整個堆建立完畢。

那麼,最下面的非葉子節點的下標是多少?

在這裏我只給出結論,有興趣的讀者可以嘗試證明一下:

當用數組表示存儲了n個元素的堆時,葉子節點的下標爲:n/2, n/2+1, … , n-1。 (n/2表示向下取整)

於是我們的調整順序爲n/2-1, … , 0:

代碼:

buildMaxHeap(int arr[], int size)
{
    for (int i = size/2-1; i >= 0; --i)
        maxHeapify(arr, i, size);
}

4、堆排序:

爲進行原地排序,我們引入另一個變量:heap_size,它用來表示堆的大小,而用size來表示數組的大小。

於是數組arr[0…size-1]中,arr[0…heap_size-1]爲堆,arr[heap_size, size-1]爲排好序的元素。

由最大堆的性質可知道,arr[0]存放着堆中最大的元素,於是可以利用該性質如下排序:

  1. 初始heap_size = size,調用buildMaxHeap(arr, heap_size)建立最大堆;
  2. 令i = size-1,交換arr[0]和arr[i],heap_size–,i–;
  3. 交換後,原來根的子女仍是最大堆,而根元素則可能違背了最大堆的性質,所以調用maxHeapify(arr, 0, heap_size)進行調整;
  4. 重複以上過程,直到堆的大小變爲2,此時再重複一次以上過程,整個數組便從小到大排好序了。
代碼:

void heapSort(int arr[], int size)
{
    if (NULL == arr || size <= 0)
        return ;
    int heap_size = size;
    buildMaxHeap(arr, heap_size);
    for (int i = size-1; i >= 1; --i)
    {
        exchange(arr[0], arr[i]);
        --heap_size;
        maxHeapify(arr, 0, heap_size);
    }
}

堆排序(heapsort)的時間複雜度爲O(n logn),不穩定。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章