新手讲排序:堆排序 和 认识二叉堆

二叉堆是完全二叉树或者是近似完全二叉树

 二叉堆满足的二个特性:

1.父节点的键值总是大于或等于(小于或等于)任何一个子节点的键值

2.每个节点的左子树和右子树都是一个二叉堆(都是最大堆或者是最小堆)

当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆,总是小于或等于任何一个子节点的键值时为最小堆

最小堆最大堆

堆的存储:

     堆的存储一般是用数组来存储,根据完全二叉树的存储结构,一个节点的父节点为(i-1)/2,一个节点的左孩子为2*i+1,右孩子为2*i+2

                                    实例

堆的建立插入和删除:

堆的建立插入删除

 

 

堆得插入:

   每次插入都是将新数据放在数组的最后,可以发现从这个新数据的父节点到根节点必然为一个有序的数列。

 

void minHeapAddNumber(int *a,int n,int nNum){
//在n位置上插入nNum这个数字 a[n]
=nNum;
//每一次找到a[n]的父节点,如果a[n]的父节点a[(n-1)/2]小于a[n],则将他们交换,继续往上找
for(int j=(n-1)/2;(j>=0&&n!=0)&&a[j]<a[n];n=j,j=(n-1)/2){ swap(a[j],a[n]); } }

 

堆的删除:

//节点从i开始,节点总数n 
//删除的时候每次删除最上面那一个节点(根节点)
//然后将最后一个元素放在根节点上
//然后根节点一次与他的左右节点进行比较,找出一个最小的,进行交换,
//知道到最后一个节点或者中间 没有元素可以交换 
void minHeapFixDown(int a[],int i,int n){
	int j,temp;
	temp=a[i];
	j=2*i+1;
	while(j<n){
		if(j+1<n&&a[j+1]<a[j]){
			j++;  //右孩子比较小 
		}
		if(a[j]>=temp) break;
		//这里很奇妙,将较小的孩子节点往上移动,
		//因为上面是和temp进行的比较,所以a[i]=temp这一步可以省略
		//只在最后出现一次 
		a[i]=a[j];
		i=j;
		j=2*i+1;
	}
	a[i]=temp;
}
void MinHeapDeleteNumber(int *a,int n){
	swap(a[0],a[n-1]); //这里已经交换过了
	MinHeapFixDown(a,0,n-1);  //所以元素个数只剩下了n-1个 
}

 

堆化数组:

    有了堆的插入和删除函数,在考虑将一个数据进行堆化操作。

例子:

 

可以看出堆化数据的时候,从最后一个非叶子节点开始,那也就是2*i+1=n-1(索引),或者2*i+2=n-1,所以可以得到 i=n/2-1;即为最后一个非叶子结点

可以得到代码:

//建立最小堆
void MakeMinHeap(int *a,int n){
    for(int i=n/2-1;i>=0;i--){
        MinHeapFixDown(a,i,n);
        //也就是把第i个节点从上面慢慢移到正确的位置
        //必须是从上到下排的,这样下面基本是有序的 
    }
} 

 

以上就是堆的基本操作。下面来讲堆排序

 

堆排序:

首先可以得到堆建好之后,第0个元素肯定是最小的,取出这个数据在执行下堆的删除,这样第0个元素又是最小的,重复上述步骤,知道堆中只剩一个元素直接取出。

 

由于堆是用数组模拟的,故堆化后,第一次将A[0] 与 A[n-1]交换,再对A[0,n-2]进行恢复,重复直到A[0]与A[1]交换,由于每次都是将最小的数据并入到后面的区间,所以操作完成后整个数组就有序了。所以使用最小堆排序后是递减数组,要得到递增数据,可以使用最大堆

//堆排序
void MinHeapSortToDescendArray(int *a,int n){
    for(int i=n-1;i>=1;i--){
        swap(a[0],a[i]);
        MinHeapFixDown(a,0,i);
    }
} 

堆排序的时间复杂度:

由于每次重新恢复堆的时间复杂度是O(logN),共N-1次重新恢复堆操作,再加上前面建立堆时N/2次向下调整,每一次时间复杂度也是O(logN);

所以堆排序的时间复杂度是O(NlogN);

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值