建堆复杂度O(n)证明
堆排序中首先需要做的就是建堆,广为人知的是建堆复杂度才O(n),它的证明过程涉及到高等数学中的级数或者概率论,不过证明整体来讲是比较易懂的。
堆排过程

代码如下
void print(vector<int> &arr)
{
for(auto n: arr) printf("%d\t", n);
cout<<endl;
}
// 以arr[n]为根的子树,将arr[n]向下调整至合适位置
void Heapify(vector<int> &arr, int size, int n)
{
int L = n*2+1, R = L+1;
if(L>=size) return ;//无孩
int big = arr[L]; // 取两孩之大者
if(R<size) big = max(big, arr[R]);
if(arr[n]>=big) return ; //无需调整
int c = L; // 欲与父交换位置的孩子
if(big!=arr[L]) c = R;
swap(arr[n], arr[c]);
Heapify(arr, size, c);
}
// 小根堆
void BuildHeap(vector<int> &arr)
{
int last = (arr.size()-1)/2;
for(int i=last; i>=0; i--) {
Heapify(arr, arr.size(), i);
}
}
// 顺便排序
void Sort(vector<int> &arr)
{
int size = arr.size();
for(int i=size-1; i>0; i--) {
swap(arr[0], arr[i]);
Heapify(arr, i, 0); //调整一下arr[0]
}
}
int main()
{
vector<int> vect{9, 10, 6, 3, 1, 6, 2, 8, 4};
print(vect); //排序前
BuildHeap(vect); //建堆
Sort(vect); //排序
print(vect); //排序后
return 0;
}
建堆的过程就是从最后一个分支结点开始逐层向上遍历,将结点向下调整至合适的位置,以不至于破坏原来的堆。比如上图,遍历的结点编号依次为3 2 1,首先调整以3为根的子树成堆,其次是以2为根的子树成堆,最后是以1为根的子树成堆。至此建堆完成,复杂度O(n)。
注意:建堆不能写成如下这样,这样的建堆算法复杂度是O(nlogn),虽然不会影响堆排序的复杂度O(nlogn),但是实现其他算法时就很不利了。
// 将arr[n]向上调整至合适位置
void AdjustHeap(vector<int> &arr, int n)
{
if(n<=0) return ;
if(arr[(n-1)/2] > arr[n]) { //与父结点比较
swap(arr[(n-1)/2], arr[n]);
AdjustHeap(arr, (n-1)/2); //递归调整
}
}
// 小根堆
void BuildHeap(vector<int> &arr)
{
for(int i=1; i<arr.size(); i++) {
AdjustHeap(arr, i);
}
}
复杂度计算
从直观上看,Heapify()的递归深度最多为\({log_n}\),故它的复杂度上限为O(logn)。而BuildHeap()中的循环为\({ \frac{n}{2} }\)次,故它的复杂度为O(nlogn),但这不是它的实际复杂度,而是一个估算的上界,它很可能永远达不到这个上界。为了方便计算,考虑结点数量为n,高度为h的满二叉树,因此\({2^h-1 = n}\),即\({h = log_2{(n+1)}}\)。
| 第几层 | 最多调整次数 | 层调整次数累计 |
|---|---|---|
| \({h}\) | \(0\) | \({2^{h-1}*0}\) |
| \({h-1}\) | \(1\) | \({2^{h-2}*1}\) |
| \({h-2}\) | \(2\) | \({2^{h-3}*2}\) |
| \(\vdots\) | \(\vdots\) | \(\vdots\) |
| \(3\) | \({ h-3 }\) | \({2^{2}*(h-3)}\) |
| \(2\) | \({ h-2 }\) | \({2^{1}*(h-2)}\) |
| \(1\) | \({ h-1 }\) | \({2^{0}*(h-1)}\) |
将最右边一列累加起来就是建堆的调整次数,则建堆的调整次数\({S(n)}\)为
\]
\]
则
\]
将(1)式减去(2)式得
\]
\]
\]
\]
又因 \({ n = 2^h-1 }\),故有
\]
注意:上面列式均是当n趋于无穷大时的计算,且(3)式是由级数的直接变换所得。其他的证明思路还有用概率的,就不写了。
写公式写到头皮发麻,写错n次了,如果错漏请不吝指正,感谢!
建堆复杂度O(n)证明的更多相关文章
- 建堆是 O(n) 的时间复杂度证明。
建堆的复杂度先考虑满二叉树,和计算完全二叉树的建堆复杂度一样. 对满二叉树而言,第 \(i\) 层(根为第 \(0\) 层)有 \(2^i\) 个节点. 由于建堆过程自底向上,以交换作为主要操作,因此 ...
- Python3实现最小堆建堆算法
今天看Python CookBook中关于“求list中最大(最小)的N个元素”的内容,介绍了直接使用python的heapq模块的nlargest和nsmallest函数的解决方式,记得学习数据结构 ...
- 堆+建堆、插入、删除、排序+java实现
package testpackage; import java.util.Arrays; public class Heap { //建立大顶堆 public static void buildMa ...
- 快速排序的期望复杂度O(nlogn)证明。
快速排序的最优时间复杂度是 \(O(nlogn)\),最差时间复杂度是 \(O(n^2)\),期望时间复杂度是 \(O(nlogn)\). 这里我们证明一下快排的期望时间复杂度. 设 \(T(n)\) ...
- 第十章 优先级队列 (b4)完全二叉堆:批量建堆
- BUG-FREE-For Dream
一直直到bug-free.不能错任何一点. 思路不清晰:刷两天. 做错了,刷一天. 直到bug-free.高亮,标红. 185,OA(YAMAXUN)--- (1) findFirstDuplicat ...
- 剑指offer面试题30:最小的k个数
一.题目描述 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. 二.解题思路 1.思路1 首先对数组进行排序,然后取出前k个数 ...
- 自己动手实现java数据结构(八) 优先级队列
1.优先级队列介绍 1.1 优先级队列 有时在调度任务时,我们会想要先处理优先级更高的任务.例如,对于同一个柜台,在决定队列中下一个服务的用户时,总是倾向于优先服务VIP用户,而让普通用户等待,即使普 ...
- 【Unsolved】线性时间选择算法的复杂度证明
线性时间选择算法中,最坏情况仍然可以保持O(n). 原因是通过对中位数的中位数的寻找,保证每次分组后,任意一组包含元素的数量不会大于某个值. 普通的Partition最坏情况下,每次只能排除一个元素, ...
随机推荐
- Redis学习笔记(3)—— 五种数据类型&keys的通用操作
一.Redis数据结构介绍 redis是一种高级的key-value的存储系统,其中的key是字符串类型,尽可能满足如下几点: 1)key不要太长,最好不要操作1024个字节,这不仅会消耗内存还会降低 ...
- DataGrip 使用--方法-..../
tip1: 关键字 自动 大写--
- 上传文件中文文件名乱码的解决方法以及iconv函数的使用
http://www.jb51.net/article/14530.htm 一般客户端是什么格式就展示什么格式,比如浏览器的UTF-8,windows的GBK
- 机器学习值KNN
- django 母版与继承
在子页面的页面最上方使用下面的语法来继承母版. {% extends 'layouts.html' %} 块: 通过在母版中使用{% block xxx %}来定义块.在子页面中通过定义母版中的blo ...
- Go语言基础环境配置(windows)
一.基础软件包安装 需要安装go环境包.git.IDE(VScode),安装包见下图: 1.1 安装go windows环境直接双击安装包安装即可,在cmd窗口输入go,结果如下图所示即表示安装成功: ...
- Sublime Text 3 3126 安装+注册码
首先,到官网下载且安装,个人是安装版本的 https://www.sublimetext.com/3 接着,写入注册码.2016/11/26 亲测有效 —– BEGIN LICENSE —– Mich ...
- apache 与php的安装
1 系统环境与软件 1 php5.5.6 下载链接:http://windows.php.net/download/#php-5.5 推荐 V11 x64,也就是64bit的. 2 apache2 ...
- 转 使用utl_http获取某个http页面内容
#########1.ACL详细解释: 11g 对于XDB UTL_HTTP or others package 的权限管控进一步加强,如果需要使用到XDB 以下包 UTL_TCP, UTL_SMT ...
- mysql查看sql语句的设置
SHOW VARIABLES LIKE "general_log%"; SET GLOBAL general_log_file = 'D:\\mysql.log'; SET GLO ...