排序算法——堆排序
来源:互联网 发布:linux tail命令详解 编辑:程序博客网 时间:2024/06/14 08:39
做为一个非专业的娱乐型选手,虽然工作了这么多年,但是和专业型选手的差距还是不能忽视,要想有进步,就必须要加倍努力。注重内在的修养。相信广大非专业选手和我一样,虽然现在也偶尔写写博客,但是以前,我觉得写博客不是我这种靠自学的人该写的。其实写博客不是为了给别人看,而是为了自己更好的理解,更好的熟悉。
- 真的开始
- 什么是堆
- 图解建堆
- 代码实现
真的开始
也不知道为什么会有这么大一段感慨,可能比较念旧吧。网上关于描述堆排序的Blog,相信多如浩瀚星辰,正如上述所说,再写这个算法,仅仅是为了自己更好的熟悉。也是因为自己太闲,想找点事做吧。
什么是堆
堆其实是一种自下向上的结构,我们可以用著名的汉诺依塔来做想像,这样一堆数据,自下向上堆积,就是堆。我在这里指的一般是二叉堆,即用一棵二叉树来描述的堆。这样说来,二叉堆,其实是一棵完全二叉树。
堆的学术定义:堆是这样一种结构,堆顶的数据大于或小于堆底的数据,并且对于堆中的其他点,依然满足前述条件,如果最大的数据处于堆顶,那么该堆则称为大顶堆,如果最小的数据处于堆顶,则相应的称为小顶堆。
那么堆排序就很显而易见了,就是通过建立大顶堆或小顶堆来给数据进行排序的一种排序算法。
图解建堆
在C++的stdlib中,有一个库函数,就是堆排序,原型如下:
int heapsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
- base:表示数据的起点
- nel:表示每个元素的大小
- width:表示每个元素的
- compar:表示一个元素比较方法,用以确定顺序
比如有一个int数组,那么heapsort的使用方法如下:
int elems[] = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7};heapsort(elems, sizeof(int), sizeof(elems) / sizeof(int), compare);
其中compare就是一个 int ()(const void, const void*)类型的函数指针
要理解堆排序算法,最好是用一个实例来演示,在此,我用上述数组来进行一次排序算法的模拟。
按顺序建立一个初始堆,如下所示:
根据堆的特性可以知道,叶子结点的无论如何都是满足特性的,所以我们从后往前,开始处理。那么第一个处理的点应该是16,该点满足堆的特性,然后处理第二点2,因为14>8>12, 所以14应处理堆顶,交换2和14的位置,如下图所示:
接下来是处理3这一点,同理10>9>3, 所以交换10和3的位置,如下所示:
然后是处理1, 同理16>14>1,所以应该交换16和1的位置,但是交换之后,下面一个堆又违反了规则,所以要继续处理该堆,最终1的位置如下:
最后处理4,处理结束之后,第一次的堆处理就已经完成了,最终处理结束的结果如下:
经过处理之后,我们可以看到,对于大顶堆来说,其中最大的值一定是位于堆顶,我们拿出顶端元素16,然后重新从数组中建立一个初始堆,重新调整数据,直至调整结束,全部数据被取出为止。
代码实现
可以看到堆的调整其实也是一个递归的过程,我这里用两个函数来实现,核心代码如下:
typedef int (*CmpFunc)(const void*, const void*);template<CmpFunc F, typename T>void adjust(T* elems, size_t nwidth, int pos){ int left = pos * 2 + 1; int right = (pos + 1) * 2; if(left >= nwidth && right >= nwidth) return ; int largest = pos; if(left < nwidth && F(elems + left, elems + pos) > 0) largest = left; if(right < nwidth && F(elems + right, elems + largest) > 0) largest = right; if(largest != pos) { T tmp = elems[largest]; elems[largest] = elems[pos]; elems[pos] = tmp; adjust<F>(elems, nwidth, largest); } return ;}template<CmpFunc F, typename T>void heapsort(T* elems, size_t nelem, size_t nwidth){ if(nwidth == 0) return ; for(int i = int(nwidth) - 1; i >= 0; --i) adjust<F>(elems, nwidth, i); heapsort<F>(++elems, nelem, --nwidth); return ;}
首先从heapsort函数开始,这个函数用于建立堆,至于是大顶堆还是小顶堆,则由CmpFunc决定,将堆中无任何元素做为出口,然后从最后一个元素开始调整,直至结束。完成调整之后,堆顶必是最大或最小的值,取出堆顶元素,重新建堆。
先从heapsort函数开始,这个函数用于建立堆,至于是大顶堆还是小顶堆,则由CmpFunc决定,共有以下几步:
1. 定义出口,若堆中元素为0, 则表示排序结束。
2. 从最后一个元素开始调整,一次调整结束之后,堆顶元素为最大值。
3. 取出堆顶元素,剩下的元素重新建堆。
可以在建堆函数中递归之前加入输出代码,来查看整个建堆的过程,如下:
for(int i = 0; i != nwidth; ++i) std::cout<< elems[i] << ","; std::cout<<std::endl;
然后是adjust函数,该函数用于调整堆,以使其符合规则,共有以下几步:
1. 取当前点的左子结点和右子结点
2. 左子结点的值和当前点值比较,取较大者(largest)
3. 右子结点的值和较大者比较,取较大者(largest)
4. 如果当前点在堆中最大,那么无需元素交换,否则,交换元素
5. 如果有元素交换,堆可能会被破坏,重新调整该点,使其符合规则。
这样,一个堆排序的函数就已经实现了,下面是一些测试数据:
第一个测试:
int compare(const void* elem1, const void* elem2){ return *((int*)elem1) - *((int*)elem2);}int elems[] = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7};heapsort<compare>(elems, sizeof(int), sizeof(elems) / sizeof(int));for(auto elem : elems) std::cout<< elem << ",";std::cout<<std::endl;
结果如下所示:
第二个测试:
char alpha[] = {'z', 'x','c','v','b','n','m','a','s','d','f','g','h','j','k','l','q','w','e','r','t','y','u','i','o','p'};heapsort<alcompare>(alpha, sizeof(char), sizeof(alpha) / sizeof(char));for(auto elem : alpha) std::cout<< elem << ",";std::cout<<std::endl;
输出结果如下:
堆排序的平均时间复杂度是O(nlgn),虽然有比他更好的快速排序,但不可否认堆排序是一个优秀的排序算法。
- 排序算法—堆排序
- 排序算法—堆和堆排序
- 排序算法——堆排序算法
- 排序算法——堆排序
- 排序算法——堆排序
- 常用排序算法——堆排序
- 排序算法——堆排序
- 排序算法——堆排序
- 算法——排序之堆排序
- 排序算法——堆排序
- 排序算法——堆排序
- 排序算法——堆排序
- 排序算法系列——堆排序
- 【排序算法】——堆排序
- 经典排序算法——堆排序
- 排序算法——堆排序
- 排序算法——堆排序
- 常用排序算法——堆排序
- 已知树的中序+后序/先序遍历,建立二叉树
- 炉石传说全套美术资源(卡牌)+UI 108M
- SpringMVC整合Mongodb开发,高级操作
- Swift 3.1 官方文档翻译(一)——关于 Swift
- Swift3.0 block 传值
- 排序算法——堆排序
- GUI_修改窗体的图标代码
- Android中去掉标题栏的3种方法
- xib 控件设置圆角 label button view 等
- MySQL 1045登录失败
- ios开发 LCProgressHUD找不到枚举类型
- 支付宝APP支付接口-PHP
- Wireshark 基本语法,及包过虑规则
- iBET Adidas Yeezy 350 V2 Lucky Draw(Adidas Yeezy 350 V2, ibet newtown casino, Newtown Casino, Newtow