赞
踩
---------------说起堆,很难不去联想到二叉树。
目录
那如何建大堆呢?或者如果 数组序列不符合 向下调整法的前提,又该怎样做?
④小插曲~ 为什么建堆的时间复杂度为O(n)? 那么堆排序的时间复杂度是多少呢?
1.堆中某个节点的数值,总是大于或小于 ”左右子树”。
2.堆总是一棵完全二叉树。
根据性质1,也就可以把堆分为大堆、小堆>_
这是一组很平常的数组. 下图所示:
把它们按照二叉树的形式分离:
可以看见,此时每个节点的值,并不满足性质1,所以此时并不构成堆。
进行这样调整后,每个节点的值,都小于左右子树的节点。
此结构为小堆.......
我们把这个方法叫做——"向下调整法”。(之后会代码实现)
但,这个方法有个前提,必须是满足左、右子树都是堆的情况。
要理解、搞懂堆排序,你不得不弄懂“向下调整法”的思想;以及代码的实现。
向下调整法:
顾名思义,从“堆顶”开始,向下调整。和两个下节点比较。建小堆就把大的数值往下调或建大堆就把小的数值往下调。
根据二叉树的性质,我们可以借助数组的下标,找到每个节点对应的位置:
我们暂时称根为父亲节点(parent),子树为孩子(child)
而,向下调整法,从上往下,是用父亲节点找孩子节点,所以设定函数参数parent好辨识。
我们很难知道到底,左右孩子谁的值小,因此就先假定左孩子小,如果不是就++child。
一旦父节点大于孩子节点,因为建小堆,大的数要往后放,所以父亲节点和孩子节点交换
并更新parent与child节点。
否则,也就是父节点的值,小于孩子节点,也就不用在调整了。因为前提是左右子树都是小堆。
注:细心的朋友或许就发现了上述代码有一处不妥。
如果,当调整一层的节点只有左孩子而没得右孩子呢?
所以我们要加上这一句,防止数组越界~
图示的关系,让我们理解透 向下调整的终止条件。
结果:小堆
从倒数第一个非叶子节点开始调整,一直调整到根节点的数。
小堆:
大堆在建堆的基础上,也就不用再去考虑左右子树是不是大堆----
改改 调整法中的大小于即可:
说起用堆排序,不同的排序方式需要不同的堆。
比如,如果要排列升序,那么就需要构建大堆!而非小堆。
完全二叉树,可以在一定程度上看成满二叉树:
堆的排序和算法到这里也就告一段落啦~
最后呢,就来写写堆的一些类似 栈、队列的功能。
堆的初始化、堆顶以及堆的有效数个数都很简单,也就不多分析。
增加数据,虽然和其他线性表大同小异,但堆毕竟是堆,不能让插入的数据,打乱堆的结构。
因此如何才能让,插入的数,到达他应该去的位置?
删除数据: 堆删除元素,我们采取去堆顶
有没有想过一个情景,给你一个N个数,要你用堆的思想,去查着N个数的钱K个大的(小的)?
面对这样的情景,我们该怎么做?
- void AdjustDown(HeapDataType* a, int n, int parent)
- {
- int child = parent * 2 + 1;
- while (child < n)
- {
- if (child + 1 < n && a[child] > a[child + 1])
- {
- child++;
- }
-
- if (a[parent] > a[child])
- {
- swap(&a[parent], &a[child]);
-
- parent = child;
- child = parent * 2 + 1;
- }
- else
- {
- break;
- }
- }
- }
-
- void CtreateData(const char* filename, int n)
- {
- FILE* fout=fopen(filename, "w");
- //把数据 放入磁盘文件
- srand((unsigned long)time(NULL));
- while (n >= 0)
- {
- fprintf(fout, "%d\n", rand()%10000);
- --n;
- }
- }
-
- void TestTopK()
- {
- //找N个数中 前K大的数
- //建议当 N 无限大 但 k 足够小时
- //建小堆 通过小堆遍历N 进行选择 替换堆顶数据 再进行向下调整
-
- //引入读文件
- const char* filename = "Data.txt";
- int N = 10000;
- //CtreateData(filename,N);
-
- FILE* fin=fopen(filename, "r");
- //读文件
- //读k个数 并建堆
- int k = 5;
- int* a =(int*)malloc(sizeof(int) * k);
- if (a == NULL)
- {
- perror("malloc");
- return;
- }
- //取数据
- for (int i = 0;i < k;++i)
- {
- fscanf(fin, "%d", &a[i]);
- }
-
- //建堆
- for (int j =(k-2)/2;j >=0 ;--j)
- {
- AdjustDown(a, k,j);
- }
-
- int val;
- while (fscanf(fin, "%d",&val) != EOF) //读文件数据
- {
- if (val > a[0])
- {
- a[0] = val;
- AdjustDown(a, k, 0);
- }
- }
-
- for (int i = 0; i < k; ++i)
- {
- printf("%d ", a[i]);
- }
- fclose(fin);
- }
谢谢你的阅读,祝你好运!~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。