堆排序

2 篇文章 0 订阅

将完全二叉树调整成堆

1. 首先,把完全二叉树存入队列中。
问题:这里为什么用队列,不用栈?
解答:因为是从根开始的,要按层遍历(逆着)取出来非叶子结点,然后这些结点和自己的子女进行比较调整。
在这里插入图片描述
结果:
1.所有元素都入队了
2.rear在最后一个元素上指着
3.front在第一个叶子节点上指着
4.end作为岗哨,为了最后输出排好的队列,因为后面删结点会用rear- - ,所以需要提前end = rear.

2. 开始转换堆
在这里插入图片描述
关键问题:

  1. 怎么找最小交换?
    解:
    首先,要清楚交换的是值 data ,不是结点。
    其次,如果此结点要去和子女比较,那么它必定有左子女,而不一定有右子女。所以,比较的时候,直接和左子女比较值。然后判断是否有右子女,再考虑是否交换。
    再者,需要标记是否进行过交换,如果这一次所有的非叶子结点都已经放好了,没有一个和子女交换,这次算是做完了,即找到了一个最小值。
  2. 删除结点时注意什么?
    解:
    如果即将砍的是左子女,需要不仅要rear- - 和 Q[front - 1]->left = NULL,还需要 front- - 。因为砍去了左子女,这个结点就是叶子结点了,下一循环不应该去和子女比较交换值。
    如果砍去的是右子女,则只需要rear- - 和 Q[front - 1] -> right = NULL。

3. 代码

# include <stdio.h>
# include <stdlib.h>

// 定义结构体 
typedef struct node {
	int data;
	struct node * left;
	struct node * right;
}BTNode;

// 函数声明: 
BTNode * CreatTree(int a[], int n);
void order(BTNode * root, int n);

int main (void){
	int a[] = {3,2,13,8,12,7,9,1,6,11,4,5}; // 完全二叉树的结点值数组 
	int n = 12;	// 数组中数的个数 
	
	BTNode * root; // 完全二叉树根结点 
	
	root = CreatTree(a,n); // 创建完全二叉树,并返回根结点 

	//堆排序,并输出排序后的结果(小根堆) 
	order(root, n); 
	
	return 0;
} 

BTNode * CreatTree(int a[], int n){
	BTNode * root;// 根结点
	BTNode * p;  // 新建的结点
	BTNode * pa; // 双亲结点
	int front = 0; // 队列的头
	int rear = 0;  // 队列的尾 
	
	// 创建队列
	BTNode ** Q = (BTNode **)malloc((2*n)*sizeof(BTNode *));
	
	// 创建根结点 
	root = (BTNode *)malloc(sizeof(BTNode));    
	root->data = a[0];
	root->left = root->right = NULL;
	Q[++rear] = root; // 根结点入队 
	
	// 创建其他结点(每次只创建一个) 
	int i ;
	for(i = 1; i < n; i++){
		pa = Q[front+1];	// 将双亲结点找到,即队首。 
		p = (BTNode *) malloc (sizeof(BTNode));
		p->data = a[i];
		p->left = p->right = NULL; 
		// 先往左放,再往右放 
		if(!(pa->left)){
			pa->left = p;
		}else {
			pa->right = p;
			pa = Q[front++];// 双亲出队 
		}
		Q[++rear] = p; // 入队 
	} 
	
	free(Q); 
	return root; 
}

void order(BTNode * root,int n){
	/* 第一步:将完全二叉树存储在队列里面,并且标记相应的值 */ 
	
		// 初始化队列 
		BTNode ** Q = (BTNode **)malloc(n * sizeof(BTNode *)); // 声明队列
		int front = 0;
		int rear = 0;
		int end = 0;
		// 根入队
		Q[++rear] = root;
		
		// 其他入队
		BTNode * pa;  
		while(1){
			pa = Q[++front]; // 取出队首
			// 查找是否有左右子女(如果是叶子结点,则循环停止)
			if(pa->left==NULL && pa->right==NULL)  break;
			if(pa->left) Q[++rear] = pa->left;
			if(pa->right) Q[++rear] = pa->right; 
		} 
		end = rear; // end 是岗哨,为了最后输出排好序的结果,标志最后一个数的位置
	 
	/*第二步:排序*/
	int tag, k;
	while(front != 1){     
		while(1){
			tag = 1;	// 标志有无交换此次
			for(k = front-1; k > 0; k--){  // 所有非叶子结点和自己的双亲进行比较,双亲中保存最小值 
				BTNode * p = Q[k];	// 取出双亲 
				BTNode * pmin = p;	// pmin 中保存最小值结点 
				if((pmin -> data) > (p->left->data))	// 一定存在左子女,所以直接和左子女比较 
					pmin = p->left;
				if(p->right)		// 判断是否存在右子女 
				{
					if(pmin->data > p->right->data)  //和右子女进行比较 
						pmin = p->right;
				}
				
				if(p != pmin)		// 判断是否需要交换 
				{
					tag = 0;		// 交换了,修改标记状态 
					int temp = p->data;
					p->data = pmin->data;
					pmin->data = temp;
				}
			}		
			if(tag) break;	//如果没有进行交换,则此次已经调整好。根放的就是最小值 
		}
		
		// 把根的值和最后一个叶子的值进行交换 
		int temp = root->data;
		root->data = Q[rear]->data;
		Q[rear]->data = temp;
		
		// 判断即将删去的最后一个结点,是左子女还是右子女 
		if(Q[front-1]->left == Q[rear])	
		{
			Q[front-1]->left = NULL;
			front--; //删除左子女时,此结点也就不再非叶子节点队列中,下次就不用去调整这个了 
		}else{
			Q[front-1]->right = NULL;
		}
		rear--;	// 最后一个结点的位置也变了 
	} 
 	
 	/*第三步:输出队列*/
 	int i;
 	for(i = end; i > 0; i--){
 		printf("%d ",Q[i]->data);
	}
}

4. 结果展示
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值