导航

1.了解什么是堆
2.如何创建最小堆,最大堆
3.新增值在堆中如何进行
4.完整的堆排序,升序和降序(两种方式)

————————————————————————————————————————

1.了解什么是堆

树型结构与图型结构的差别:

看是否有回路,无回路的是树型,有回路的是图型

堆  最小堆 最大堆  堆排序(小到大,大到小)_堆

满二叉树可以用数组进行存储,层次遍历顺序存储
设置其中父节点为k,儿子节点(左结点,右节点) 其中左节点 为2*k右节点为 2 * k+1
高度为log2^N

其中最典型的应用就是(也就是完全二叉树)
堆  最小堆 最大堆  堆排序(小到大,大到小)_最小堆_02
完全二叉树
堆  最小堆 最大堆  堆排序(小到大,大到小)_最小值_03

————————————————————————————————————————

2.如何将堆转化为最小堆,最大堆

有两层的直接用父结点来比较
三层的应该有倒数一层的右边节点开始依次往下
堆  最小堆 最大堆  堆排序(小到大,大到小)_父节点_04

代码如下:(创建最小堆)

//这里用的是数组的方式存储堆
void siftdown(int i)  //输入的是从i点往下找
{
	int t,flag=0;  //flag用来表示无需再进行往下查找 
	//从i位置开始,判断是否有左孩子,并且没有找到最佳位置 
	while(i*2<=n&&flag==0)
	{
		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){  //判断得到最小值是否为父节点,是的话flag标志1,不是的话交换,继续往下找 
			swap(t,i);
			i=t;
		}
		else
			flag = 1;
	}
}

创建最大堆:只需要将父节点与左右孩子节点的判大小符号换一下就可以了

————————————————————————————————————————

3.新增值在堆中如何进行

先插入到堆中最后一个位置,在进行与父节点依次比对,找到最合适的位置
堆  最小堆 最大堆  堆排序(小到大,大到小)_数组_05
整个都用数组保存
代码:

//这里弄得是最小堆 
void siftup(int i)
{
	int i,flag=0;   //flag等于0表示当前子节点比父节点小 
	if(i==1) return;   //如果为根节点直接返回 
	while(i!=1&&flag==0)   
	{ 
		if(h[i]<h[i/2])
			swap(i,i/2);
		else
			flag=1;
		i=i/2;    //让子节点等于父节点的位置 
	}
} 

————————————————————————————————————————

4.完整的堆排序,升序和降序

输入提示:
14
99 5 36 7 22 17 46 12 2 19 25 28 1 92

升序:

#include <stdio.h>
int h[101]; //用来存放堆的数组
int n;  //存放堆的个数

void swap(int x,int y)
{
	int t;
	t = h[x];
	h[x] = h[y];
	h[y] = t;
} 

void siftdown(int i)
{
	int t,flag=0;  //flag用来表示无需再进行往下查找 
	//从i位置开始,判断是否有左孩子,并且没有找到最佳位置 
	while(i*2<=n&&flag==0)
	{
		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){  //判断得到最小值是否为父节点,是的话flag标志1,不是的话交换,继续往下找 
			swap(t,i);
			i=t;
		}
		else
			flag = 1;
	}
}

void create()
{
	int i;
	for(i=n/2;i>=1;i--)
		siftdown(i);
}

//用于返回最小值
int deletemax()
{
	int t;
	t=h[1];
	h[1]=h[n];
	n--;   //删除一位
	siftdown(1);  //进行最小堆
	return t;
}
 
int main()
{
	int i,num;
	scanf("%d",&num);
	for(i=1;i<=num;i++)
		scanf("%d",&h[i]);
	n = num;
	
	//建立最小堆
	create();
	
	//删除顶部元素,接着将最后一个元素放入第一个顶点中,输出删除的元素
	for(i=1;i<=num;i++)
		printf("%d ",deletemax()); 
	return 0;
} 

其中deletemax()函数作用是每次返回最小值,并且将最后一个值移入到根节点再次进行最小堆,这样结束时数组中数据已不再是原来的。
若想用这样的来求降序也是可以的,只需要建立最大堆就行了,其余都一样

另一种方式来求解升序和降序

//之前创建的是最大堆 
void headsort()
{
	while(n>1)
	{
		swap(1,n);     //将第一个与最后一个进行交换,此时最后一个点为最大值 
		n--;     //下次建立最大堆不带上最后一个值 
		siftdown(1);  //进行最大堆 
	}
	
}

求解升序另一种方法完整

#include <stdio.h>
int h[101]; //用来存放堆的数组
int n;  //存放堆的个数

void swap(int x,int y)
{
	int t;
	t = h[x];
	h[x] = h[y];
	h[y] = t;
} 

void siftdown(int i)
{
	int t,flag=0;  //flag用来表示无需再进行往下查找 
	//从i位置开始,判断是否有左孩子,并且没有找到最佳位置 
	while(i*2<=n&&flag==0)
	{
		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){  //判断得到最小值是否为父节点,是的话flag标志1,不是的话交换,继续往下找 
			swap(t,i);
			i=t;
		}
		else
			flag = 1;
	}
}

//开始创建堆 
void create()
{
	int i;
	for(i=n/2;i>=1;i--)
		siftdown(i);
}

//之前创建的是最大堆 
void headsort()
{
	while(n>1)
	{
		swap(1,n);     //将第一个与最后一个进行交换,此时最后一个点为最大值 
		n--;     //下次建立最大堆不带上最后一个值 
		siftdown(1);  //进行最大堆 
	}
	
}
 
int main()
{
	int i,num;
	scanf("%d",&num);
	for(i=1;i<=num;i++)
		scanf("%d",&h[i]);
	n = num;
	
	//建立最小堆
	create();
	
	headsort(); 
	
	//删除顶部元素,接着将最后一个元素放入第一个顶点中,输出删除的元素
	for(i=1;i<=num;i++)
		printf("%d ",h[i]); 
	return 0;
}