堆排序的二三事

来源:互联网 发布:数据迁移的重要性 编辑:程序博客网 时间:2024/06/02 19:28

1.可以用做外排序。

比如查找N个数据(假设数据量内存放不下)中最大的前K个

取这N个数据中的前K个建一个小堆,

因为是小堆,所以,堆顶数据必然是最小的

此时,读取剩余数据,只要是大于堆顶数据,就交换堆顶数据和读到的数据。

然后调整堆,让堆顶继续是最小数据。

(永远保证堆里面保存着读之前所有数据中最大的前K个)

一直读取,直到所有数据读结束。 此时堆里就保存着这N个数据中最大的前K个。

如果是最小的前K个,同理,建大堆。


2.堆也可以直接对一组数据排序。

如果是排成升序建大堆

大堆保证了堆顶数据是最大的,且每个节点都是大堆

堆顶既然已经是最大的数据,堆顶的数据已经有序,所以把堆顶的数据放在数组最后面(因为是升序)

再用原堆底的数据来填补堆顶。(即交换 堆顶和堆低)。

此时再对除堆低以外的数据继续建大堆,然后交换堆低和堆顶,直到只剩最后一个数据,

此时数组有序。

同理,排降序,则建小堆





1.最大前K个数据如下:

#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>#include <windows.h>#include <vector>#include <assert.h>#include <utility>using namespace std;template<class T>class Less{public:bool operator()(const T& left, const T& right)  const{return left < right;}};template<class T>class Great{public:bool operator()(const T& left, const T& right)  const{return left > right;}};template<class T, class Compare = Great<T>>class Heap{public:Heap(){}Heap(T* a, size_t n){_a.reserve(n);for (size_t i = 0; i < n; i++){_a.push_back(a[i]);}//建堆for (int i = (_a.size() - 2) / 2; i >= 0; --i){AdjustDown(i);}}void AdjustDown(int root)   //向下调整{Compare com;int parent = root;int child = parent * 2 + 1;while (child < _a.size()){if (child + 1 < _a.size() && com(_a[child + 1], _a[child]))   //选出 最大的一个孩子{++child;}if (com(_a[child], _a[parent]))     //让父亲是最大的{swap(_a[child], _a[parent]);parent = child;child = child * 2 + 1;}else{break;}}}void AdjustUp(int child)              //向上调整{Compare com;int parent = (child - 1) / 2;while (){if (com(_a[parent], _a[child])){swap(_a[parent], _a[child]);child = patent;parent = (child - 1) / 2;}else{break;}}}protected:vector<T> _a;};void  AdjustDown(int* heap, int n, int root){assert(heap);int parent = root;int child = parent * 2 + 1;while (child < n){if (child + 1 < n && heap[child + 1] < heap[child]){++child;}if (heap[child] < heap[parent]){swap(heap[child], heap[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}void TopK(){const size_t N = 10000;const size_t K = 10;int a[N] = { 0 };for (size_t i = 0; i < N; i++){a[i] = rand() % N;}a[0] = N + 100;a[100] = N + 101;a[2] = N + 102;a[50] = N + 105;a[1000] = N + 100;a[1005] = N + 1550;a[888] = N + 130;a[9998] = N + 100;a[9999] = N + 100;a[9444] = N + 100;int heap[K] = { 0 };for (size_t i = 0; i < K; i++){heap[i] = a[i];}// 建小堆   堆顶为最小数据for (int i = (K - 2) / 2; i >= 0; i--){AdjustDown(heap, K, i);}//大于堆顶的数据,放入堆  调整一次for (int i = K; i < N; i++){if (a[i] > heap[0]){heap[0] = a[i];AdjustDown(heap, K, 0);}}for (size_t i = 0; i < K; i++){cout << heap[i] << " ";}cout << endl;}int main(){int a[] = { 10,11, 13, 12, 16, 18, 15, 17, 14, 19 };Heap<int>hp(a, sizeof(a) / sizeof(a[0]));TopK();system("pause");return 0;}




2.最大前K个

void AdjustDown(int a[], int root, int len)        //向下调整   //{assert(a);int parent = root;int child = parent * 2 + 1;while (child < len)                  //检查孩子节点是否存在{if (child + 1 < len && a[child] < a[child + 1]){child++;}if (a[parent] < a[child])         //孩子中最大的那个大于父节点则交换 (大堆,必须保证父节点是最大的){swap(a[child], a[parent]);parent = child;            //检查孩子节点 是否是大堆child = parent * 2 + 1;}elsebreak;}}void HeapSort(int a[], int len) //升序,建大堆{assert(a);for (int i = (len - 1) / 2; i >= 0; i--){AdjustDown(a, i, len);       //建大堆,必须保证根节点(堆顶)是最大的。}int end = len;while (end > 0)               {swap(a[end], a[0]);       //用最后一个节点替换根节点。AdjustDown(a, 0, end);end--;                    //此时最后一个节点已经是最大节点(已经有序),去掉最后一个节点。}}


原创粉丝点击