原文:https://blog.youkuaiyun.com/LeoSha/article/details/46116959
先看下堆排序与快速排序的实现代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=111;
int a[maxn];
void quicksort(int a[],int l,int r)
{
if(l>=r) return;
int i,j,tmp;
i=l;
j=r;
tmp=a[l];
while(i!=j)
{
while(j>i&&a[j]>=tmp) j--;
while(i<j&&a[i]<=tmp) i++;
swap(a[i],a[j]);
}
a[l]=a[i];
a[i]=tmp;
quicksort(a,l,i-1);
quicksort(a,i+1,r);
}
void adjustheap(int num[],int s,int len)
{
int tmp=num[s];
int child=s*2+1;
while(child<len)
{
if(child+1<len&&num[child]<num[child+1])
child++;
if(num[child]>num[s])
{
num[s]=num[child];
num[child]=tmp;
s=child;
child=s*2+1;
}
else
break;
}
}
void buildheap(int num[],int len)
{
for(int i=(len-1)/2;i>=0;i--)
{
adjustheap(num,i,len);
}
}
void heapsort(int num[],int len)
{
buildheap(num,len);
for(int i=len-1;i>0;i--)
{
swap(num[i],num[0]);
adjustheap(num,0,i);
}
}
int main()
{
int n,i;
scanf("%d",&n);
//for(i=1;i<=n;i++)
//scanf("%d",&a[i]);
//quicksort(a,1,n);
for(i=0;i<n;i++)
scanf("%d",&a[i]);
heapsort(a,n);
for(i=0;i<n;i++)
printf("%d ",a[i]);
puts("");
return 0;
}
牛客网测试排序代码链接:https://www.nowcoder.com/questionTerminal/508f66c6c93d4191ab25151066cb50ef
如果仅从代码上直观观察,会得出构造二叉堆的时间复杂度为O(n㏒n)的结果,这个结果是错的,虽然该算法外层套一个n次循环,而内层套一个分治策略下的㏒n复杂度的循环,该思考方法犯了一个原则性错误,那就是构建二叉堆是自下而上的构建,每一层的最大纵深总是小于等于树的深度的,因此,该问题是叠加问题,而非递归问题。那么换个方式,假如我们自上而下建立二叉堆,那么插入每个节点都和树的深度有关,并且都是不断的把树折半来实现插入,因此是典型的递归,而非叠加。
在做证明之前,我们的前提是,建立堆的顺序是bottom-top的。
正确的证明方法应当如下:
具有n个元素的平衡二叉树,树高为㏒n,我们设这个变量为h。
最下层非叶节点的元素,只需做一次线性运算便可以确定大根,而这一层具有2^(h-1)个元素,我们假定O(1)=1,那么这一层元素所需时间为2^(h-1) × 1。
由于是bottom-top建立堆,因此在调整上层元素的时候,并不需要同下层所有元素做比较,只需要同其中之一分支作比较,而作比较次数则是树的高度减去当前节点的高度。因此,第x层元素的计算量为2^(x) × (h-x)。
又以上通项公式可得知,构造树高为h的二叉堆的精确时间复杂度为:
S = 2^(h-1) × 1 + 2^(h-2) × 2 + …… +1 × (h-1) ①
通过观察第四步得出的公式可知,该求和公式为等差数列和等比数列的乘积,因此用错位想减发求解,给公式左右两侧同时乘以2,可知:
2S = 2^h × 1 + 2^(h-1) × 2+ …… +2 × (h-1) ②
用②减去①可知: S =2^h × 1 - h +1 ③
将h = ㏒n 带入③,得出如下结论:
S = n - ㏒n +1 = O(n)
结论:构造二叉堆的时间复杂度为线性得证。
原文:https://blog.youkuaiyun.com/LeoSha/article/details/46116959