C++树&二叉树&堆&基于最小堆的堆排序

先看一下树和二叉树

在这里插入图片描述
这就是一棵树,最上面那个是祖先,叫根结点,在上面的是父结点,下面的是子结点,最下面的那排没儿子了,所以叫叶结点,根结点和叶结点中间那些就叫内部结点。根结点深度为1,然后依次类推,在第几层深度就是几。
很明显,树不包含回路。
在这里插入图片描述

二叉树

二叉树就是每个结点最多有俩儿子–左儿子和右儿子。
二叉树有俩特殊的情况,满二叉树和完全二叉树,满二叉树就是内部结点都有俩儿子,很饱满。完全二叉树就是叶结点从右向左缺了几个(因为必须有左儿,要不就没儿,不能光一个右儿)如图,就是一颗完全二叉树
在这里插入图片描述
完全二叉树有很多特性,比如父亲编号k,儿子编号就是2k和2k+1,反之儿子编号是x,x+1,他们的爹编号就是x/2;而且完全二叉树最后一个非叶结点编号为n/2。

堆,就是特殊的完全二叉树,有最小堆和最大堆,最小堆就是所有的爹都比它儿子要小,最大堆就是所有的爹都比它儿子要大。但是儿子和儿子之间没啥必然的大小关系。
在这里插入图片描述

堆排序

其实,堆排序强烈不建议用,光写代码就累死,直接sort不好吗?只需要知道原理即可!
用堆排序,首先得建堆,以最小堆为例

void creat()
{
	for(i=i/2;i>=1;i--)
	{
		siftdown(i);
	}
}

为啥在n/2处开始呢,因为大于n/2就是叶结点了,不用管了。
siftdown函数就是向下调整的函数,一开始整个堆不满足最小堆的定义,所以向下调整

void siftdown(int i)
{
	int t,flag=0;
	while(flag==0&&i*2<=n)
	{
		if(h[i]>h[i*2])//用数组h来存结点,h[i]的值就是某节点的值
		{
			t=i*2;
		}
		else t=i;
		if(i*2+1<=n)
		{
			if(h[t]>h[i*2+1])
			{
				t=i*2+1;
			}
		}
		if(t!=i)
		{
			swap(i,t);
			i=t;
		}
		else flag=1;
	}
	return;
}

swap函数就是交换的作用,交换两者的h[i]值

void swap(int x,int y)
{//没用t,用了一种新方法
	h[x]=h[x]+h[y];
	h[y]=h[x]-h[y];
	h[x]=h[x]-h[y];
	return;
}

堆建好了,然后呢,怎么排序呢?咱们建立的是最小堆,所以其根结点就是最小值,所以只要把根结点和最后一个叶结点交换数值,再把最后一个叶结点弹出,这就获得了最小的一个值。然后接着向下调整(调用siftdown函数),让它符合最小堆特性,符合了以后再把根结点弹出…一直这样操作就可以进行排序了

int popmin()
{
	int t;
	t=h[1];
	h[1]=h[n];
	n--;
	siftdown(1);
	return t;
}

完整代码如下

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int h[100],n;
void swap(int x,int y)
{
	h[x]=h[x]+h[y];
	h[y]=h[x]-h[y];
	h[x]=h[x]-h[y];
	return;
}
void siftdown(int i)
{
	int t,flag=0;
	while(flag==0&&i*2<=n)
	{
		if(h[i]>h[i*2])
		{
			t=i*2;
		}
		else t=i;
		if(i*2+1<=n)
		{
			if(h[t]>h[i*2+1])
			{
				t=i*2+1;
			}
		}
		if(t!=i)
		{
			swap(i,t);
			i=t;
		}
		else flag=1;
	}
	return;
}
void creat()
{
	for(int i=n/2;i>=1;i--)
	{
		siftdown(i);
	}
	return;
}
int popmin()
{
	int t;
	t=h[1];
	h[1]=h[n];
	n--;
	siftdown(1);
	return t;
}
int main()
{ 
	int num;
	cin>>num;
	for(int i=1;i<=num;i++)
	{
		cin>>h[i];
	}
	n=num;
	creat();
	for(int i=1;i<=num;i++)
	{
		cout<<popmin()<<" ";
	}
	return 0;
}
/*
14
99 5 36 7 22 17 46 12 2 19 25 28 1 92
*/
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值