[编程珠玑读书笔记]堆排序,小根堆

本文通过实现堆排序算法并与其他系统排序方法进行效率对比,展示了堆排序的高效性和适用场景。通过5行代码实现核心逻辑,堆排序在大规模数据集上的表现优于传统系统排序,特别适用于对时间和资源敏感的应用。

堆排序的关键是要实现siftup和siftdown。当建立完这两个函数以后,排序一个数组只需要5行代码。算法执行了n-1次siftup和siftdown,而每次操作的成本最多O(lgn),所以运行时间为O(nlogn)。

#include <stdio.h> #include <stdlib.h> #define MAX 20 void swap( int *data, int i, int j) { int temp = data[i]; data[i] = data[j]; data[j] = temp; } //siftup比较好理解,将每一个元素都与自己的父亲比较,如果自己的值小于父亲的值,就互换,直到到堆顶,或父亲的值小于自己的值为止。 void siftup(int *data, int n ) { int i = n; int p; while( 1 ) { if ( i == 1 ) break; p = i/2; if( data[p] <= data[i]) break; swap( data, i, p ); i = p; } } //这里的n其实意义不大,是指n之后的数据是符合要求的,n之前的数据可能不满足小根堆的要求,调整的方法也是从堆顶开始,初步向小调整 void siftdown( int *data, int n) { int i = 1; int c = 0; while( 1 ) { c = 2 * i; if( c > n ) break; //取两个孩子中较小的一个与自己作比较 if( c + 1 <= n && data[ c + 1] < data[c] ) c++; //如果孩子的值小于自己的值,则互换 if( data[i] <= data[c] ) break; swap( data, c, i); i = c; } } int main() { int data[ MAX + 1]; int i= 0; srand(5); for( i = 1; i <= MAX; i++ ) data[i] = rand() % 500; //建堆 for( i = 2; i <= MAX; i++ ) siftup(data,i); //从后向前调整 for( i =MAX; i >= 2; i--) { swap(data, 1, i); siftdown(data, i - 1 ); } for( i = 1; i < MAX; i++ ) printf("%d\t", data[i]); printf("\n"); return 0; }


堆排序与系统排序的效率比较:

#include <stdio.h> #include<iostream> #include<string.h> #include <algorithm> #include <stdlib.h> using namespace std; #define MAX 200000 void swap( int *data, int i, int j) { int temp = data[i]; data[i] = data[j]; data[j] = temp; } //siftup比较好理解,将每一个元素都与自己的父亲比较,如果自己的值小于父亲的值,就互换,直到到堆顶,或父亲的值小于自己的值为止。 void siftup(int *data, int n ) { int i = n; int p; while( 1 ) { if ( i == 1 ) break; p = i/2; if( data[p] <= data[i]) break; swap( data, i, p ); i = p; } } //这里的n其实意义不大,是指n之后的数据是符合要求的,n之前的数据可能不满足小根堆的要求,调整的方法也是从堆顶开始,初步向小调整 void siftdown( int *data, int n) { int i = 1; int c = 0; while( 1 ) { c = 2 * i; if( c > n ) break; //取两个孩子中较小的一个与自己作比较 if( c + 1 <= n && data[ c + 1] < data[c] ) c++; //如果孩子的值小于自己的值,则互换 if( data[i] <= data[c] ) break; swap( data, c, i); i = c; } } int main() { double BegTime, EndTime; int data[ MAX + 1]; int data2[ MAX + 1]; data2[0] = 0; int i= 0; srand(5); for( i = 1; i <= MAX; i++ ) data[i] = rand() % 500; memcpy( data2, data, MAX+1); BegTime = clock(); //建堆 for( i = 2; i <= MAX; i++ ) siftup(data,i); //从后向前调整 for( i =MAX; i >= 2; i--) { swap(data, 1, i); siftdown(data, i - 1 ); } EndTime = clock(); printf("HeapSort:%gms\n", (EndTime - BegTime) / 1000); BegTime = clock(); sort(data2, data2 + MAX + 1); EndTime = clock(); printf("sort: %gms\n", (EndTime - BegTime) / 1000); printf("\n"); return 0; }
测试结果如下:

HeapSort:60ms
sort: 40ms

结果表明,堆排序确实有着相当不错的表现,不到必须的时候,还是应当使用系统的sort函数,效率很高,且节约开发成本。

小根堆是一种常用的数据结构,它可以用来在一个未排序的数组中找到最小的 k 个元素,或者用来排序一个数组。下面是一个简单的实现小根堆的代码: ```python class MinHeap: def __init__(self, maxsize=None): self.maxsize = maxsize self._elements = [None] * (maxsize + 1) self._count = 0 def __len__(self): return self._count def add(self, value): if self._count + 1 <= self.maxsize: self._count += 1 self._elements[self._count] = value self._siftup(self._count) def _swap(self, i, j): self._elements[i], self._elements[j] = self._elements[j], self._elements[i] def _siftdown(self, idx): left = idx * 2 right = idx * 2 + 1 smallest = idx if left <= self._count and self._elements[left] < self._elements[smallest]: smallest = left if right <= self._count and self._elements[right] < self._elements[smallest]: smallest = right if smallest != idx: self._swap(idx, smallest) self._siftdown(smallest) def _siftup(self, idx): parent = idx // 2 if parent > 0 and self._elements[idx] < self._elements[parent]: self._swap(idx, parent) self._siftup(parent) def extract(self): if self._count > 0: value = self._elements[1] self._elements[1] = self._elements[self._count] self._count -= 1 self._siftdown(1) return value ``` 这里我们实现了一个最小堆,它可以用来找到一个未排序的数组中最小的 k 个元素。我们可以先将数组中的前 k 个元素加入到堆中,然后把剩下的元素逐个堆顶元素进行比较,如果比堆顶元素小,则把堆顶元素替换为该元素,并进行下滤操作。最终堆中的元素就是最小的 k 个元素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值