数据结构——最小最大堆(C语言)

来源:互联网 发布:java接收文件 编辑:程序博客网 时间:2024/06/18 08:44

一、定义

    最小-最大堆:是一棵完全二叉树,二叉树的各层交替为最小层和最大层,且根节点位于最小层。

    最大层:该层上的节点大于等于以其为根节点的子树上的所有节点。

    最小层:该层上的节点小于等于以其为根节点的子树上的所有节点。

二、性质

  1. 处于最小层节点的关键字值逐层增大,这里是指按树本身节点的联系顺序而非层次遍历的顺序。
  2. 处于最大层节点的关键字值逐层减小。
  3. 所有处于最大层节点的关键字值大于所有处于最小层节点的关键字值。
                                            

三、最小-最大堆的插入操作

    注:堆的层次(深度)编号从1开始,根节点编号也从1开始。

    用min_max_insert()函数将数据item插入到堆中,插入前节点个数i=*n;

   步骤:

  1. 最小-最大堆节点个数i=i+1;并求出节点i的父节点parent=i/2;判断父节点parent处于最大层还是最小层。
  2. 若父节点parent处于最小层:
    1. 当所插入数据item小于父节点parent的关键字值时,把父节点的关键字值赋给当前节点的关键字值,即heap[i]=heap[parent];然后把数据item插入到以parent节点向上到根节点的最小层中,即调用verify_min(heap,parent,item);
    2. 若所插入数据item不小于父节点parent的关键字值时,把数据item插入到以当前节点i向上到根节点的最大层中,即调用verify_max(heap,i,item);
  3. 若父节点parent处于最大层:
    1. 当所插入数据item大于父节点parent的关键字值时,把父节点的关键字值赋给当前节点的关键字值,即heap[i]=heap[parent];然后把数据item插入到以parent节点向上到根节点的最大层中,即调用verify_max(heap,parent,item);
    2. 若所插入数据item不大于父节点parent的关键字值时,把数据item插入到以当前节点i向上到根节点的最小层中,即调用verify_min(heap,i,item);

    例如在以下最小-最大堆中插入数据item=5;插入后节点数i=13;则父节点parent=i/2=6;父节点关键字值为heap[parent]=heap[6]=10;且父节点parent位于最小层,因此5<10;则利用步骤2(1);


/**最小-最大堆插入操作**/void min_max_insert(int *heap,int *n,int item){    int parent;    int i;    i=(*n);    i++;    if(MAX_SIZE==i)//判断是否为满堆    {        printf("it is full.\n");        exit(1);    }    parent=(i)/2;//第一个非叶子结点,即结点i的父结点    if(!parent)//判断是否为空堆        heap[1]=item;//序号从1开始,而不是0    else    {      int k;      k=level(parent);//判断parent结点在最大层还是最小层      switch(k)//k=0为最小层,1为最大层      {          case 0:                        if(item<heap[parent])//插入结点的数据与其父结点比较                        {                            heap[i]=heap[parent];//交换                            verify_min(heap,parent,item);//插入在parent结点向上到根                                                        //的最小层中                        }                        else                            verify_max(heap,i,item);                        break;          case 1:                        if(item>heap[parent])                        {                            heap[i]=heap[parent];                            verify_max(heap,parent,item);                        }                        else                            verify_min(heap,i,item);                        break;      }    }    *n=i;}

四、最小-最大堆的删除操作

    这里以删除最小元素为例。最小-最大堆的根节点元素为最小,则把根节点删除,并调整好堆,使其成为新的最小-最大堆。

    一般情况下,把最后一个节点关键值为item重新插入到根节点为空的最小-最大堆中,分以下两种情况讨论:

  1. 根节点没有儿子。此时,可把item直接插入根节点。
  2. 根节点至少有一个儿子节点。此时,最小-最大堆的关键字值最小节点(不包括item)一定位于根节点的儿子节点或后代节点。设最小关键字值节点的下标为k,则需讨论以下几种情况:
    • a)item<=heap[k]时,此时由于堆中不存在比item更小的关键字值,则可把item直接插入到根节点。
    • b)item>heap[k]k是根节点的儿子节点,此时,由于k是最大节点,所以其后代节点中不可能存在关键字值大于heap[k]的节点,从而也没有关键字值大于item的节点,所以把关键字值heap[k]移到根节点,并将item插入k节点中。
    • c)item>heap[k]k是根节点的后代节点,此时,可以把关键字值heap[k]移到根节点,并将item插入k节点中,然后考察k节点的父亲节点parent:如果item>heap[parent],则将itemheap[parent]交换,此时,问题转化为将item插入到以k为根节点的子堆中,而且此子堆的根节点heap[k]已经腾空,这时将item插入到以1为根节点的最小-最大堆而根节点1为空的情况相似。

    例子:在此最小-最大堆中item=12,且根节点的所有儿子和后代节点的最小关键字值为9,设关键字值为9节点的下标为k,其父节点下标为parent。因为9<12;且k节点是根节点的后代节点,则属于情况2(c),此时把heap[k]=9移到根节点,由于item=12<heap[parent]=70;则把item直接插入到k节点中。


/*最小最大堆删除操作,并返回最小元素*/int delete_min_heap(int *heap,int *n){    if(!(*n))    {        printf("The heap is empty.\n");        exit(1);    }    int item,temp;    int k,parent;    int i,last;    int x;    x=heap[1];//根节点关键字值赋给x    item=heap[(*n)--];//最后节点的值赋给item    i=1;    last=(*n)/2;//为重新插入item初始化    /*寻找插入item的位置*/    while(i<=last)//有儿子或后代节点,情况2    {        k=min_child_grandchild(i,*n);//返回最小元素的节点下标        if(item<=heap[k])break;//如情况2(a),直接插入到根节点        else        {            heap[i]=heap[k];//把关键字值移到根节点            if(k<=2*i+1)//k是i的直接儿子,情况2(b)            {                i=k;//把item插入到heap[k]                break;            }            else//k是i的后代节点,情况2(c)            {                parent=k/2;                if(item>heap[parent])                    SWAP(item,heap[parent],temp);                i=k;            }        }    }    heap[i]=item;    return x;}

五、程序

   函数声明:
#ifndef FUNCTION_H_INCLUDED#define FUNCTION_H_INCLUDED#define SWAP(x,y,t) ((t)=(x),(x)=(y),(y)=(t))#define MAX_SIZE 10int heap[MAX_SIZE];void verify_max(int *heap,int i,int item);void verify_min(int *heap,int i,int item);int level(int node);//判断结点属于最大层还是最小层void min_max_insert(int *heap,int *n,int item);int delete_min_heap(int *heap,int *n);int min_child_grandchild(int i,int n);#endif // FUNCTION_H_INCLUDED
   函数定义:
#include<math.h>#include <stdio.h>#include <stdlib.h>#include "function.h"/**从最大结点i开始,沿着从i到根结点路径,依次检查最大结点**//**查找插入数据item的正确结点,这个结点应有以下性质:从该结点向上到根的路径上所有最大结点的关键字值都不小于item,而且从结点i向下到该结点的路径上所有最大关键字值都小于item.**//**查找过程中,关键字值小于item的最大结点被下移到下一个最大层**/void verify_max(int *heap,int i,int item){    int grandparent=i/4;//第一个非叶子结点    while(grandparent)    {        if(item<=heap[grandparent])            break;        else        {            heap[i]=heap[grandparent];            i=grandparent;            grandparent/=4;        }    }    heap[i]=item;}/**从最小结点i开始,沿着从i到根结点路径,依次检查最小结点**//**查找插入数据item的正确结点,这个结点应有以下性质:从该结点向上到根的路径上所有最小结点的关键字值都不大于item,而且从结点i向下到该结点的路径上所有最小关键字值都大于item.**//**查找过程中,关键字值大于item的最小结点被下移到下一个最小层**/void verify_min(int *heap,int i,int item){    int grandparent=i/4;    while(grandparent)    {        if(item>=heap[grandparent])            break;        else        {            heap[i]=heap[grandparent];            i=grandparent;            grandparent/=4;        }    }    heap[i]=item;}int level(int node)//判断结点属于最大层还是最小层{    double k=log(node)/log(2);    if((int)k%2==0)return 0;//最小层返回0    else return 1;//最大层返回1}/**最小-最大堆插入操作**/void min_max_insert(int *heap,int *n,int item){    int parent;    int i;    i=(*n);    i++;    if(MAX_SIZE==i)//判断是否为满堆    {        printf("it is full.\n");        exit(1);    }    parent=(i)/2;//第一个非叶子结点,即结点i的父结点    if(!parent)//判断是否为空堆        heap[1]=item;//序号从1开始,而不是0    else    {      int k;      k=level(parent);//判断parent结点在最大层还是最小层      switch(k)//k=0为最小层,1为最大层      {          case 0:                        if(item<heap[parent])//插入结点的数据与其父结点比较                        {                            heap[i]=heap[parent];//交换                            verify_min(heap,parent,item);//插入在parent结点向上到根                                                        //的最小层中                        }                        else                            verify_max(heap,i,item);                        break;          case 1:                        if(item>heap[parent])                        {                            heap[i]=heap[parent];                            verify_max(heap,parent,item);                        }                        else                            verify_min(heap,i,item);                        break;      }    }    *n=i;}/*确定节点i所有儿子和后代节点的最小关键字值*/int min_child_grandchild(int i,int n){    int child,temp,t;    int j;    child=2*i;//i的儿子节点    temp=heap[child];//保存当前值    while(child<=n)//节点下标小于节点数    {        for(j=child;j<=n;j++)//遍历所有儿子节点和后代节点        {            if(temp>heap[j+1])                {                    temp=heap[j+1];//若当前节点的儿子节点或后代节点存在小于当前关键字值,把该节点赋给当前节点                t=j+1;//保存最小节点的下标                }        }        child=j;    }    return t;}/*最小最大堆删除操作,并返回最小元素*/int delete_min_heap(int *heap,int *n){    if(!(*n))    {        printf("The heap is empty.\n");        exit(1);    }    int item,temp;    int k,parent;    int i,last;    int x;    x=heap[1];//根节点关键字值赋给x    item=heap[(*n)--];//最后节点的值赋给item    i=1;    last=(*n)/2;//为重新插入item初始化    /*寻找插入item的位置*/    while(i<=last)//有儿子或后代节点,情况2    {        k=min_child_grandchild(i,*n);//返回最小元素的节点下标        if(item<=heap[k])break;//如情况2(a),直接插入到根节点        else        {            heap[i]=heap[k];//把关键字值移到根节点            if(k<=2*i+1)//k是i的直接儿子,情况2(b)            {                i=k;//把item插入到heap[k]                break;            }            else//k是i的后代节点,情况2(c)            {                parent=k/2;                if(item>heap[parent])                    SWAP(item,heap[parent],temp);                i=k;            }        }    }    heap[i]=item;    return x;}
   测试程序:
#include <stdio.h>#include <stdlib.h>#include"function.h"int main(){    int i,item;    int n=0;    for(item=1;item<10;item++)        min_max_insert(heap,&n,item);//插入节点    for(i=1;i<10;i++)        printf("%d ",heap[i]);        printf("\n");    item=delete_min_heap(heap,&n);//删除节点,并返回最小元素    printf("The deleted data is %d\n",item);    for(i=1;i<=n;i++)        printf("%d ",heap[i]);    return 0;}


0 0