堆--优先队列

来源:互联网 发布:网络投影仪怎么用 编辑:程序博客网 时间:2024/06/16 20:37

     在进入主题之前, 先让我们来了解一下我们需要掌握的预备知识:树、二叉树。

     有学习过算法的同学应该都听过图这个名词吧,树与图挺类似的,唯一的区别就是图是连同的,但树是不连通的,也就是任意两个节点之间有且只有一条路径。就像我们计算机文件夹的结构,如果我们要从一个文件夹到另一个文件夹,有且只有一条路径。

     那二叉树又是什么呢?首先,二叉树是树,但它是特殊的数。二叉树的每一个节点最多只有两个子节点,左边的叫做左节点,右边的叫做右节点。二叉树又分为满二叉树与完全二叉树。满二叉树中每一个内部节点都具有两个儿子;如图:满二叉树;完全二叉树除最高层外,其他各层节点数达到最大值, 同时;最高层只允许从右向左有连续若干缺节点;如图:完全二叉树,性状就类似与完全二叉树。二叉树为什么这么重要呢?二叉树具有什么样特殊的性质呢?笔者认为二叉树最重要的性质就是其父节点与子节点的关系了:如果父节点为 i 且其有子节点,则左子节点为 2*i ;右子节点为 (2*i+1);如果子节点为 i ;不论其是左子节点还是右子节点,其父节点为 (i/2)取整。根据这个性质;我们就可以用一维数组来存储一棵树了。

         二叉树这个特殊的数据结构能给我们带来什么样的奇特功能呢?先让我们来看一张图:二叉树数据结构。不知道细心的你有没有发现什么玄机呢?是的,任何一个父节点都比它的子节点有小,这叫做最小二叉树;还有一种是任何一个父节点都比它的子节点的的,称为最大二叉树。如果我们这样安排数据,那会给我们什么样的方便呢?是的,如果我们这样做了,我们就可以准确的知道最小的数或最大的数在那,这样我们就不用遍历整个数组去找最大最小的数了。

       下面我们讲一下怎样对二叉树进行操作。先讲插入:如果我们要插入一个数,我们先将这个数添加到数组的尾部,然后与其父节点比较,若小于父节点,就交换;交换后继续与对应的父节点比较,若还小,则继续交换,知道满足二叉树的性质。如果要删除最小的数呢?首先,我们把最后的一个数放在数组首部,让 其成为整个二叉树的根,然后在从根部一步步向下调整。那要怎么调整呢?首先,我们要让其先与左节点比较(因为若有子节点,则必有左节点);然后在与右节点比较;若都满足,则与最小的节点交换;重复次操作,直到满足二叉树的性质。

       说了这么多,那我们要怎样实现一个二叉树呢?首先,我们先把所有的数据存进一个数组。在进行下面的操作之前,我们应知道:最后一个非叶节点为(n/2)。我们要把一颗杂乱的数整理成符合要求的二叉树;我们只需从最后一个非叶节点开始向下调整,直到根节点,则整理后的二叉树就是符合要求的二叉树。


全部代码如下:    

#include<stdio.h>int h[101];int n;void swap(int x,int y){     //用于交换两个数int temp=h[x];h[x]=h[y];h[y]=temp;} void siftup(int i){       //用于插入一个数,向上调整int 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;}}void siftdown(int i){     //向下调整int t,flag=0;while(i*2<=n&&flag==0){if(h[i]>h[2*i]){   t=2*i;}else   t=i;if(i*2+1<=n){if(h[t]>h[2*i+1])   t=2*i+1;}if(t!=i){   swap(t,i);   i=t;   //继续循环,直至符合二叉树性质 }else  flag=1;}}void creat(){      //创建二叉树for(int i=n/2;i>=1;i--){siftdown(i);}} int deletemin(){     //用于取出最小的元素   int t=h[1];   h[1]=h[n--];   siftdown(1);   //重新调整二叉树    return t;}int main(){int num;scanf("%d",&num);n=num;for(int i=1;i<=n;i++){scanf("%d",&h[i]);} creat();for(int i=1;i<=num;i++){printf("%d ",deletemin());} return 0;} 










1 0
原创粉丝点击