堆排序原理及其实现(C++)
https://blog.csdn.net/lzuacm/article/details/52853194
堆排序原理及其實現(C++)
1. 堆排序的引入
我們知道簡單選擇排序的時間復雜度為O(n^2),熟悉各種排序算法的朋友都知道,這個時間復雜度是很大的,所以怎樣減小簡單選擇排序的時間復雜度呢?簡單選擇排序主要操作是進行關鍵字的比較,所以怎樣減少比較次數就是改進的關鍵。簡單選擇排序中第i趟需要進行n-i次比較,如果我們用到前面已排好的序列a[1...i-1]是否可以減少比較次數呢?答案是可以的。舉個例子來說吧,A、B、C進行比賽,B戰勝了A,C戰勝了B,那么顯然C可以戰勝A,C和A就不用比了。正是基于這種思想,有人提出了樹形選擇排序:對n個記錄進行兩兩比較,然后在([n/2]向上取整)個較小者之間在進行兩兩比較,如此重復,直到選出最小記錄。但是這種排序算法需要的輔助空間比較多,所以威洛姆斯(J . Willioms)在1964年提出了另一種選擇排序,這就是下面要談的堆排序。
2. 什么是堆
首先堆heap是一種數據結構,是一棵完全二叉樹且滿足性質:所有非葉子結點的值均不大于或均不小于其左、右孩子結點的值.
3. 堆排序思想
堆排序的基本思想是利用heap這種數據結構(可看成一個完全二叉樹),使在排序中比較的次數明顯減少。
堆排序的時間復雜度為O(n*log(n)), 非穩定排序,原地排序(空間復雜度O(1))。
堆排序的關鍵在于建堆和調整堆,下面簡單介紹一下建堆的過程:
第1趟將索引0至n-1處的全部數據建大頂(或小頂)堆,就可以選出這組數據的最大值(或最小值)。將該堆的根節點與這組數據的最后一個節點交換,就使的這組數據中最大(最小)值排在了最后。
第2趟將索引0至n-2處的全部數據建大頂(或小頂)堆,就可以選出這組數據的最大值(或最小值)。將該堆的根節點與這組數據的倒數第二個節點交換,就使的這組數據中最大(最小)值排在了倒數第2位。
…
第k趟將索引0至n-k處的全部數據建大頂(或小頂)堆,就可以選出這組數據的最大值(或最小值)。將該堆的根節點與這組數據的倒數第k個節點交換,就使的這組數據中最大(最小)值排在了倒數第k位。
其實整個堆排序過程中, 我們只需重復做兩件事:
-
建堆(初始化+調整堆, 時間復雜度為O(n));
-
拿堆的根節點和最后一個節點交換(siftdown, 時間復雜度為O(n*log n) ).
因而堆排序整體的時間復雜度為O(n*log n).
下面通過一組數據說明堆排序的方法:
9, 79, 46, 30, 58, 49
1: 先將待排序的數視作完全二叉樹(按層次遍歷順序進行編號, 從0開始),如下圖:
2:完全二叉樹的最后一個非葉子節點,也就是最后一個節點的父節點。最后一個節點的索引為數組長度len-1,那么最后一個非葉子節點的索引應該是為(len-1)/2.也就是從索引為2的節點開始,如果其子節點的值大于其本身的值。則把他和較大子節點進行交換,即將索引2處節點和索引5處元素交換。交換后的結果如圖:
建堆從最后一個非葉子節點開始即可
3:向前處理前一個節點,也就是處理索引為1的節點,此時79>30,79>58,因此無需交換。
4:向前處理前一個節點,也就是處理索引為0的節點,此時9 < 79,9 < 49, 因此需交換。應該拿索引為0的節點與索引為1的節點交換,因為79>49. 如圖:
5:如果某個節點和它的某個子節點交換后,該子節點又有子節點,系統還需要再次對該子節點進行判斷。如上圖因為1處,3處,4處中,1處的值大于3,4出的值,所以還需交換。
牢記:?將每次堆排序得到的最大元素與當前規模的數組最后一個元素交換。
偽代碼如下:
1、由于是完全二叉樹, 故有:
PARENT(i)return i / 2 LEFT(i)return 2 * i RIGHT(i)2 * i + 1- 1
- 2
- 3
- 4
- 5
- 6
- 7
2、Heapify?
以最大堆為例,偽代碼:
MAX-HEAPIFY(A, i)
l = LIFT(i) r = RIGHT(i) if l <= A.heapsize and A[l] > A[i]largest = l else largest = i if r <= A.heapsize and A[r] > A[largest]largest = r if largest != iexchage A[i] with A[largest]MAX-HEAPIFY(A, largest)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
3、Build Heap?
以最大堆為例,偽代碼:
BUILD-MAX-HEAP(A)
A.heap-size = A.length for A.length / 2 downto 1MAX-HEAPIFY(A, i)- 1
- 2
- 3
- 4
4、Heapsort?
以最大堆為例,偽代碼:
HEAPSORT(A)?
BUILD-MAX-HEAP(A)
- 1
- 2
- 3
- 4
- 5
C++完整代碼:
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std;void adjust(int arr[], int len, int index) {int left = 2*index + 1;int right = 2*index + 2;int maxIdx = index;if(left<len && arr[left] > arr[maxIdx]) maxIdx = left;if(right<len && arr[right] > arr[maxIdx]) maxIdx = right; // maxIdx是3個數中最大數的下標if(maxIdx != index) // 如果maxIdx的值有更新{swap(arr[maxIdx], arr[index]);adjust(arr, len, maxIdx); // 遞歸調整其他不滿足堆性質的部分}} void heapSort(int arr[], int size) {for(int i=size/2 - 1; i >= 0; i--) // 對每一個非葉結點進行堆調整(從最后一個非葉結點開始){adjust(arr, size, i);}for(int i = size - 1; i >= 1; i--){swap(arr[0], arr[i]); // 將當前最大的放置到數組末尾adjust(arr, i, 0); // 將未完成排序的部分繼續進行堆排序} }int main() {int array[8] = {8, 1, 14, 3, 21, 5, 7, 10};heapSort(array, 8);for(auto it: array){cout<<it<<endl;}return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
為何堆排序是不穩定排序?
當數組中有相等元素時,堆排序算法對這些元素的處理方法不止一種,故是不穩定的。
可重載比較函數的寫法:
#include<memory.h> #include<stdio.h> #include<stdlib.h> void swap(void* x, void* y, size_t sz) {void* t = malloc(sz);memcpy(t, x, sz);memcpy(x, y, sz);memcpy(y, t, sz);free(t); } void makeHeap(void* x, int i, int n, size_t sz, int(*cmp)(const void*, const void*)) {char* y = (char*)x;int l = 2 * i + 1;int r = 2 * i + 2;int m;if (l<n && (*cmp)(y + l*sz, y + i*sz)>0) m = l;else m = i;if (r<n && (*cmp)(y + r*sz, y + m*sz)>0) m = r;if (m != i){swap(y + i*sz, y + m*sz, sz);makeHeap(x, m, n, sz, cmp);} } void buildHeap(void* x, int n, size_t sz, int(*cmp)(const void*, const void*)) {for (int i = n / 2 - 1; i >= 0; i--) makeHeap(x, i, n, sz, cmp); } void heapSort(void* x, int n, size_t sz, int(*cmp)(const void*, const void*)) {buildHeap(x, n, sz, cmp);char* y = (char*)x;for (int i = n - 1; i >= 1; i--){swap(y, y + i*sz, sz);makeHeap(x, 0, --n, sz, cmp);} }void p(int* x,int n){for (int k = 0; k < n; k++){printf("%d ", x[k]);}printf("\n"); }int less(const void* a, const void* b){return *((int*)a) < *((int*)b); } int greater(const void* a, const void* b){return *((int*)a) > *((int*)b); } int main(){int x[] = { 2, 3, 4, 6, 8, 2, 9, 0 };// 降序全排列heapSort(x, 8, sizeof(int), less);p(x, 8);// 升序全排列heapSort(x, 8, sizeof(int), greater);p(x, 8);// 最大的4個元素,在數組末尾heapSort(x, 4, sizeof(int), less);p(x, 8); }總結
以上是生活随笔為你收集整理的堆排序原理及其实现(C++)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网站网页设计怎么收费(网页设计如何收费)
- 下一篇: 第1步 新建spring+spring