排序 - 选择排序之堆排序

堆:(也叫优先队列),可以看作一棵完全二叉树。特点:父结点与子节点的关键字之间有固定的大小关系。常用于管理算法执行过程中的信息,应用场景包括堆排序,优先队列等。

分类:

  1. 最大堆(大顶堆):父节点的值大于等于两个子节点的值,常用于堆排序。
  2. 最小堆(小顶堆):父节点的值小于等于两个子节点的值,常用于构造优先队列。

堆排序是一种树形选择排序,在排序过程中,将待排序的记录a[n]看作一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子节点之间的内在关系,在当前无序序列中选出关键字最大的记录,故是一种特殊的选择排序。

首先需要注意:为什么堆排序可执行排序?       

因为在将二叉树按行存储在以1为第一个下标的线性表时,其父结点与左右子女节点存在固定关系:父结点下标为x,则左子女下标为2*x,右子女下标为2*x+1。则当堆构建成最大堆时,总有当前下标的记录的关键字小于下标比其小的记录的关键字。这时只能保证其前一半有序,那么就还需要保证后一半有序,则便需要不停的交换,调整,最后将所有的元素变得都有序。便排序完成。

实现堆排序时的步骤:

  1. 建立初始堆:将无需序列(无序二叉树)构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;(creat)

  2. 将堆顶元素与末尾元素交换,将最大(最小)元素"沉"到数组末端;(change)

  3. 调整堆:在堆顶元素改变后,将剩余元素调整成一个新的最大堆。(adjustment)
  4. 反复执行调整+交换步骤,直到整个序列有序。

值得注意的是:堆排序不一定得创建一个二叉树,利用属组的下标关系一样可以实现堆结构。

首先思考如何调整堆:调整堆的目的是使的一个只有根结点不满足堆特性的二叉树转换成一个大根堆或小根堆。

那么以其他的节点为根结点的二叉树都满足堆特性,这时只需要将当前待调整的节点(当前根结点)与其子女节点比较,将它们中最大的节点与根结点交换,这时,当前根结点就满足了堆特性,然而新的问题出现了,可能交换后,以子女节点为根的二叉树又不满足堆特性了。这时,子女节点就面临了与开始根结点相同的境遇,这时我们就需要一个循环(或者递归)来解决这个问题。那么循环的终止条件又是什么?首先,我们操作的过程是要选出父结点,子女节点中最大的一项,则当父结点已经是最大节点是,便不需要交换,也就不会引入新的问题,那么问题结束。但是有可能父结点足够小,不断的交换,不断的产生新的问题,这时也不必担心。因为最终循环交换的结果是此节点必然会交换到叶子结点的位置,那么它必然就是以它为跟中最大的节点了。

C语言实现调整:

void adjustment (int a[],int i,int N){//参数分别代表:a:数组,i:待调整元素,N:数组长度
	int left=2*i,right=2*i+1;
	int max=i,t;
	
	if(left<N&&a[left]>a[i]&&a[left]>=a[right]){	//当节点的下标超过数组长度是,表示此节点不存在,即此条件为,必须存在左节点,并且左节点值大于父结点 
		max=left;
	} 
	if(right<N&&a[right]>a[i]&&a[right]>=a[left]){
		max=right;
	}
	if(max!=i){
		t=a[i];
		a[i]=a[max];
		a[max]=t;		//交换当前值与最大值,交换过后最大值的位置可能引入新的问题 
		
		adjustment(a,max,N);	//调用调整 函数 
	}
	
} 

创建:根据调整,我们可以得到:可以将一个只有根结点不满足堆结构的二叉树调整为一个堆结构,那么,就可以由小到大的创建一个堆,即假设一个只有两层的二叉树,肯定满足调整的要求,那么可以通过调整使其成为堆结构,这时将其规模扩大到三层,因为第二层,第三层之间关系已经满足堆特性,即只有根结点需要调整,满足调整要求。依此递归,即任意的二叉树都可以通过不断的调整使其称为一个堆,这个过程称为创建堆。

值得注意的是:一个二叉树的最后一个父结点的位置等于,总节点数除2,向下取整。

void creat(int a[],int N){
	int i=N/2;
	for(i;i>0;i--){
		adjustment(a,i,N);
		//Print(a,11);
	}
}

交换与调整:

void Sort (int a[],int N){
	int t; 
	creat(a,11);//创建大顶堆
	for(N;N>0;N--){//与最末尾的元素交换,交换后待排元素-1 
		t=a[1];
		a[1]=a[N-1];
		a[N-1]=t;
		adjustment (a,1,N);
	}	
}

整体C语言描述:

#include <stdio.h>
//选择排序之堆排序

 
void Print(int a[],int N){
	for(int i=1;i<N;i++){
		printf("%d\t",a[i]);
	}
	printf("\n");
}
 

void adjustment (int a[],int i,int N){//参数分别代表:a:数组,i:待调整元素,N:数组长度
	int left=2*i,right=2*i+1;
	int max=i,t;
	
	if(left<N&&a[left]>a[i]&&a[left]>=a[right]){	//当节点的下标超过数组长度是,表示此节点不存在,即此条件为,必须存在左节点,并且左节点值大于父结点 
		max=left;
	} 
	if(right<N&&a[right]>a[i]&&a[right]>=a[left]){
		max=right;
	}
	if(max!=i){
		t=a[i];
		a[i]=a[max];
		a[max]=t;		//交换当前值与最大值,交换过后最大值的位置可能引入新的问题 
		
		adjustment(a,max,N);	//调用调整 函数 
	}
	
} 

void creat(int a[],int N){
	int i=N/2;
	for(i;i>0;i--){
		adjustment(a,i,N);
		//Print(a,11);
	}
}

void Sort (int a[],int N){
	int t; 
	creat(a,11);//创建大顶堆
	for(N;N>0;N--){//与最末尾的元素交换,交换后待排元素-1 
		t=a[1];
		a[1]=a[N-1];
		a[N-1]=t;
		adjustment (a,1,N);
	}	
}



int main (void){//测试代码 
	int a[11]={0,4,1,3,2,16,9,10,14,8,7};//第一个元素0不参与排序
	Print(a,11);
	creat(a,11);
	Print(a,11);
}



 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值