导航
1.了解什么是堆
2.如何创建最小堆,最大堆
3.新增值在堆中如何进行
4.完整的堆排序,升序和降序(两种方式)
——————————————————————————————————————
1.了解什么是堆
树型结构与图型结构的差别:
看是否有回路,无回路的是树型,有回路的是图型
满二叉树可以用数组进行存储,层次遍历顺序存储
设置其中父节点为k,儿子节点(左结点,右节点) 其中左节点 为2*k, 右节点为 2 * k+1
高度为log2^N
其中最典型的应用就是堆(也就是完全二叉树)
完全二叉树:
——————————————————————————————————————
2.如何将堆转化为最小堆,最大堆
有两层的直接用父结点来比较
三层的应该有倒数一层的右边节点开始依次往下
代码如下:(创建最小堆)
//这里用的是数组的方式存储堆 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.新增值在堆中如何进行
先插入到堆中最后一个位置,在进行与父节点依次比对,找到最合适的位置
整个都用数组保存
代码:
//这里弄得是最小堆 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; }