堆排序详细说明以及模板代码

堆是一棵完全二叉树,根据树中每个结点和其左右子结点的关系可分为大顶堆(结点不小于左右子结点),小顶堆(结点不大于左右子结点)
堆初始化是将值存储在一个一维数组heap中,从下标1到n,可以根据这个顺序想象成一棵完全二叉树。heap[1]对应的根结点

举例说明(构造大顶堆的过程)

例如heap[5]={2,6,4,3,1},可以想象成这样的一棵完全二叉树,箭头可以直接忽略。。

2
6
4
3
1

由于大顶堆需要上面的元素不小于下面的,所以只需要针对有子结点的结点进行判断,这又是一棵完全二叉树,结点的个数n,对应的最后的有子结点的下标是n/2。
这里n=5,n/2=2,也就是6这个结点,进行向下调整对应的 void downAdjust(int low,int high) 函数
这里向下调整有两个方向,向左和向右。
找到两个子结点中最大的那个与该结点比较,如果子结点大于该结点,就把该结点和对应子结点交换。否则就忽视。
这里6的子结点是1和3,最大的3,但是3<6,不做变动。
对2号结点(6)处理完毕,未发生变化。

接下来对1号结点(2)进行处理,依然是向下调整。
找到6和4中最大的,是2号结点(6),交换2号结点(6)和1号结点(2)的值。

6
2
4
3
1

此时2号结点的值(2),再将2号结点(2)进行向下调整。2号结点对应的子结点有4号结点(3)和5号结点(1)。两个中间最大的为4号结点(3),3>2 交换4号结点(3)和2号结点(2)的位置,如下

6
3
4
2
1

此时大顶堆就形成了。可以看出所有子结点都不大于其父结点。heap[5]={6,3,4,2,1}
但是大顶堆还并不是一个有序的序列。
堆排序需要对大顶堆再次进行调整。
将除1号结点外的每个结点与1号结点i进行交换,然后按照向下调整,调整范围为1到i-1,

构造大顶堆的时候调整范围是从i到n

步骤:

  1. 将5号结点(1)与1号结点(6)进行交换,此时向下调整范围为下标1到4
1
3
4
2
6

1.1 找到1号结点(1)子结点中最大的,3号结点(4),4>1交换两个结点

4
3
1
2
6

1.2 3号结点(1)下面没有子结点,该次向下调整结束
2. 将4号结点(2)与1号结点(4)进行交换,此时调整范围为下标1到3

2
3
1
4
6

2.1 找到1号结点(2)子结点中最大的,2号结点(3),3>2,交换两个结点.

3
2
1
4
6

2.2 找到2号结点(2)子结点在1到3中范围最大的,没有,所以结束。
3. 将3号结点(1)与1号结点(3)进行交换,此时调整范围为下标1到2

1
2
3
4
6

3.1 找到1号结点(1)子结点中在1到2范围最大的,2号结点(2),交换两个结点

之前漏了加黑的这个条件是在前面的,然后找到最大的子结点为3号结点(3),再判断超出了范围结束了调整,这是错误的,然后发现最后交换2和1,排序错误

2
1
3
4
6
  1. 将2号结点(1)与1号结点(2)进行交换,此时调整范围为下标1到2
1
2
3
4
6

堆排序结束。

实现代码
//向下调整
void downAdjust(int low,int high){ 
	int i=low,j=i*2;
	while(j <= high){
		if(j+1<=high && heap[j+1]>heap[j]){
			j=j+1;
		}
		if(heap[j]>heap[i]){
			swap(heap[j],heap[i]);
			i=j;
			j=i*2;
		}else{
			break;
		}
	}
}
//构建大顶堆
void createHeap(){
	for(int i=n/2;i>=1;i--){
		downAdjust(i,n);
	}
}
//向上调整
void upAdjust(int low,int high){
	int i=high,j=i/2;
	while(j>=low){
		if(heap[j]<heap[i]){
			swap(heap[j],heap[i]);
			i=j;
			j=i/2;	
		}else{
			break;
		}
	}
}
//向大顶堆中插入数据x
void insert(int x){
	heap[++n]=x;
	upAdjust(1,n);
}
//堆排序
void heapSort(){
	createHeap();
	for(int i=n;i>1;i--){
		swap(heap[i],heap[1]);
		downAdjust(1,i-1);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值