堆
堆是一种数据结构,是完全二叉树在数组中的存储。这棵完全二叉树满足:任何一个非叶节点的值都不大于(或不小于)其左右孩子的节点的值。若父亲的值大,孩子的值小,这样的堆叫做大顶堆;若父亲的值小,孩子的值大,则这样的堆叫做小顶堆。
由堆得定义知道,代表堆得这棵完全二叉树的根节点的值是最大(最小的),然后将找出的这个值交换到序列的最后(最前),这样有序序列元素增加1个,无序序列元素减少1个,对无序序列重复这样的操作,由此实现排序。
实现代码如下,
#include <iostream>
using namespace std;
void sift(int nums[],int low,int high)//调整堆
{
int i=low,j=low*2;
int temp=nums[i];
while(j<=high)
{
if( j<high && nums[j]<nums[j+1] )//右节点若存在,且右节点较左节点大
++j; //则将j指向较大的节点
if( temp<nums[j] )//nums[j]子节点比根节点大
{
nums[i]=nums[j];//把子节点调整到根节点的位置上
i=j;//重新设置根节点。把子节点的位置赋值为根节点
j=i*2;//j重新指向左子节点
}
else //temp>nums[j],则代表以i为根节点的树,已经满足定义了
break;
}
nums[i]=temp;//将i位置赋值为原先"根节点"的值
}
void heapSort(int nums[],int lastPos)
{
for(int n=lastPos/2;n>=0;--n) //调整为最大堆,从(lastPos/2)位置开始
sift(nums,n,lastPos);
for(int i=lastPos;i>0;--i)//调整为堆之后,
{
int temp=nums[0]; //nums[0]中存储最大值
nums[0]=nums[i]; //并把最后的元素调整到第一个元素
nums[i]=temp;//将最大值调整的最后
sift(nums,0,i-1);//再次调整堆,使其符合大顶堆得定义
}
}
int main()
{
int nums[]={8,7,6,5,4,3,2,1};
heapSort(nums,7);//heap排序,下标从0开始,共7个元素。
for(int i=0;i<8;i++)
cout<<nums[i]<<" ";
return 0;
}
复杂度分析
(1)时间复杂度
对于sift()
函数,下标j
走了一条从当前结点(不一定是根节点,但是也有可能从某个叶节点开始调整,比如[1,2,3,4,5]
将从下标为5/2=2的位置开始,而该位置是叶节点),完全二叉树的高度为( (int)log2(n) )+1
,对每个节点调整的时间复杂度为O(log2(n))
,其中2为底数。对于heapSort()
函数,第一个循环n/2次,第二循环次数为(n-1)
。根据大O定义,可知其时间复杂度为O(nlog2(n))
。
(2)空间复杂度
仅使用有限个变量,所以空间复杂度为O(1)
。