資料結構與演算法 - 堆排序
阿新 • • 發佈:2022-03-01
堆排序
顧名思義,是利用堆這種資料結構來進行排序的演算法。
如果你瞭解堆這種資料結構,你應該知道堆是一種優先佇列,兩種實現,最大堆和最小堆,由於我們這裡排序按升序排,所以就直接以最大堆來說吧。
我們完全可以把堆(以下全都預設為最大堆)看成一棵完全二叉樹,但是位於堆頂的元素總是整棵樹的最大值,每個子節點的值都比父節點小,由於堆要時刻保持這樣的規則特性,所以一旦堆裡面的資料發生變化,我們必須對堆重新進行一次構建。
既然堆頂元素永遠都是整棵樹中的最大值,那麼我們將資料構建成堆後,只需要從堆頂取元素不就好了嗎? 第一次取的元素,是否取的就是最大值?取完後把堆重新構建一下,然後再取堆頂的元素,是否取的就是第二大的值? 反覆的取,取出來的資料也就是有序的資料。
我們以[ 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); } }