1. 程式人生 > 其它 >資料結構與演算法 - 堆排序

資料結構與演算法 - 堆排序

堆排序

顧名思義,是利用堆這種資料結構來進行排序的演算法。

如果你瞭解堆這種資料結構,你應該知道堆是一種優先佇列,兩種實現,最大堆和最小堆,由於我們這裡排序按升序排,所以就直接以最大堆來說吧。

我們完全可以把堆(以下全都預設為最大堆)看成一棵完全二叉樹,但是位於堆頂的元素總是整棵樹的最大值,每個子節點的值都比父節點小,由於堆要時刻保持這樣的規則特性,所以一旦堆裡面的資料發生變化,我們必須對堆重新進行一次構建。

既然堆頂元素永遠都是整棵樹中的最大值,那麼我們將資料構建成堆後,只需要從堆頂取元素不就好了嗎? 第一次取的元素,是否取的就是最大值?取完後把堆重新構建一下,然後再取堆頂的元素,是否取的就是第二大的值? 反覆的取,取出來的資料也就是有序的資料。

我們以[ 8,2,5,9,7,3 ]這組資料來演示。

首先,將陣列構建成堆。

既然構建成堆結構了,那麼接下來,我們取出堆頂的資料,也就是陣列第一個數 9 ,取法是將陣列的第一位和最後一位調換,然後將陣列的待排序範圍 -1。

現在的待排序資料是[ 3,8,5,2,7 ],我們繼續將待排序資料構建成堆。

取出堆頂資料,這次就是第一位和倒數第二位交換了,因為待排序的邊界已經減 1 。

繼續構建堆

從堆頂取出來的資料最終形成一個有序列表,重複的步驟就不再贅述了,我們來看一下程式碼實現。

程式碼實現

void sort(vector<int>& arr)
{
    int length = arr.size();
    buildHeap(arr, length);
}

void buildHeap(vector<int>& arr, int length)
{
    for(int i = length/2; i >=0; i--) {
        sink(arr, i, length);
    }
}

// 下沉調整
void sink(vector<int>& arr,int index, int length)
{
    int leftChild = 2 * index +1; // 左孩子下標
    int rightChild = 2 * index + 2; // 右孩子下標
    int present = index;    // 要調整的節點下標

    // 下沉左邊
    if(leftChild < length && arr[leftChild] > arr[present]) {
        present = leftChild;
    }

    // 下沉右邊
    if(rightChild < length && arr[rightChild] > arr[present]) {
        present = rightChild;
    }

    // 如果下標不想等 證明調換過了
    if (present != index)
    {
        // 交換值
        int temp = arr[index];
        arr[index] = arr[present];
        arr[present] = temp;

        // 繼續下沉
        sink(arr, present, length);
    }

}