广工数据结构实验——堆(C语言实现)

效果图


1. 题目

采用顺序存储结构来实现抽象数据类型

数据对象:D{ ai | ai∈RcdType, i=1,2,...,n,  n≥0 } 

数据关系:其所有非叶子结点均不大于(或不小于)其左右孩子结点。即按完全二叉树的结点编号排列,n个结点的关键字序列(k1,k2,,kn)称为堆,当且仅当满足以下关系:

小顶堆:ki<=k2i,ki<=k2i+1    

大顶堆:ki>=k2i,ki>=2i+1      (i=1,2,,n/2(向下取整))

基本操作

1InitHeap(Heap &H,int size,int tag)

初始条件:堆H已声明,tag已赋值为10size由操作者赋好值

操作结果:初始化一个最大容量为size的空堆H,当tag01时分别表示小顶堆和大顶堆,成功初始化就返回OK

2MakeHeap(Heap &H,RcdType *E,int n,int size,int tag)

初始条件:堆H已声明,E[1..n]是堆的n个结点,0号单元闲置,n为堆的长度,tag01

操作结果:用E建长度为n的堆H,容量为size,当tag01时分别表示小顶堆和大顶堆。

3RemoveFirstHeap(Heap &H,RcdType &e)

初始条件:堆H已存在。

操作结果:删除堆H的堆顶结点且用e来返回。 若H.n<0返回ERROR,成功就返回OK

4RemoveHeap(Heap &H,int pos,RcdType &e)

初始条件:堆H已存在,pos>0&&pos<=H.n

操作结果:删除位置pos的结点,用e返回其值。 成功返回OK,否则返回ERROR

5swapHeapElem(Heap &H,int i,int j)

初始条件:堆H已存在,ij都大于0且小于等于H.n

操作结果:交换堆H中的第i结点和第j结点,初始条件符合且交换成功返回OK,否则返回ERROR

6DestroyHeap(Heap &H)

初始条件:堆H已存在。

操作结果:销毁堆H。 成功返回OK

7InsertHeap(Heap &H,RcdType e)

初始条件:堆H已存在。

操作结果:将结点e插入到堆H中,且不改变H堆的特性。成功返回OK,堆已满则返回ERROR

8ShiftDown(Heap &H,int pos)

初始条件:堆H已存在,pos结点的左右子树都是堆。

操作结果:对堆H中位置为pos的结点做筛选,将以pos为根的子树调整为子堆。

9printHeap(Heap H)

初始条件:堆H已存在

操作结果:把H用顺序存储结构和树的表示在屏幕上打印出来

    10HeapSort(RcdSqList &L,int tag)

初始条件:L[1..n]存要排序的记录,tag分别用10表示大顶堆或小顶堆

操作结果:对L[1..n]进行对排序,完成时L中记录是有序的,若tag=1则是从小到大排,否则从大到小排。

11greatPrior(KeyType x,KeyType y)

操作结果:大顶堆用的优先函数,若x>=y,返回值为1,否则为0

12lessPrior(KeyType x,KeyType y)

操作结果:小顶堆用的优先函数,若x<=y,返回值为1,否则为。


#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0

typedef int Status;
typedef int KeyType;    //关键字类型为整数类型
typedef struct{
    KeyType key;        //关键字项

}RecordType,RcdType;    //记录类型,RcdType为RecordType的简写
typedef struct{
    RcdType *rcd;    //存储空间基址
    int length;      //当前长度
    int size;        //存储容量
}RcdSqList;//记录的顺序表
typedef struct{
    RcdType *rcd;       //堆基址,0号单元闲置
    int n;              //堆长度
    int size;           //堆容量
    int tag;            //小顶堆与大顶堆的标志:tag=0为小顶堆,tag=1为大顶堆
}Heap;  //堆类型

int greatPrior(KeyType x,KeyType y){    //大顶堆优先函数
    return x>=y;
}

int lessPrior(KeyType x,KeyType y){     //小顶堆优先函数
    return x<=y;
}

//初始化一个最大容量为size的空堆H,当tag为0或1时分别表示小顶堆和大顶堆
Status InitHeap(Heap &H,int size,int tag){
    H.rcd=NULL;
    H.size=size;
    H.n=0;
    H.tag=tag;
    return OK;
}

//交换堆H中的第i结点和第j结点
Status swapHeapElem(Heap &H,int i,int j){
    RcdType temp;
    if(i<=0||i>H.n||j<=0||j>H.n){
        return ERROR;
    }
    temp=H.rcd[i];
    H.rcd[i]=H.rcd[j];
    H.rcd[j]=temp;
    return OK;

}
//对堆H中位置为pos的结点做筛选,将以pos为根的子树调整为子堆,前提是pos结点的左右子树都是堆
void ShiftDown(Heap &H,int pos){
    int t,rt;
    int (*prior)(KeyType,KeyType);
    if(H.tag==1)    prior=greatPrior;
    else    prior=lessPrior;

    while(pos<=H.n/2){        //2*pos>n时没有左孩子,更没有右孩子,是叶子结点
        t=pos*2;            //pos的左孩子位置
        rt=pos*2+1;         //pos的右孩子位置
        if(rt<=H.n&&prior(H.rcd[rt].key,H.rcd[t].key))
            t=rt;               //t为pos结点的左右孩子中较为优先者的位置
        if(prior(H.rcd[pos].key,H.rcd[t].key))
            return;             //若pos结点较优先,则筛选结束
        swapHeapElem(H,pos,t);//否则pos和较优先者t交换位置
        pos=t;              //继续向下调整
    }
}

//用E建长度为n的堆H,容量为size,当tag为0或1时分别表示小顶堆和大顶堆
void MakeHeap(Heap &H,RcdType *E,int n,int size,int tag){
    int i;
    H.rcd=E;    //E[1..n]是堆的n个结点,0号单元闲置
    H.n=n;
    H.size=size;
    H.tag=tag;
    int (*prior)(KeyType,KeyType);
    if(H.tag==1)    prior=greatPrior;
    else    prior=lessPrior;
    for(i=H.n/2;i>0;i--)     //对所有非叶子结点筛选
        ShiftDown(H,i);
}

//销毁堆H
Status DestroyHeap(Heap &H){
    free(H.rcd);
    H.rcd=NULL;
    H.n=0;
    H.size=0;
    return OK;
}

//将e插入堆,从堆尾插入
Status InsertHeap(Heap &H,RcdType e){
    int curr;
    if(H.n>=H.size-1||H.size==0) return ERROR;//堆已满,插入失败
    //问题点
    if(H.n==0){
        H.rcd=(RcdType*)malloc(sizeof(RcdType)*H.size);
        H.rcd[1]=e;
    }
    curr=++H.n;
    H.rcd[curr]=e;
    int (*prior)(KeyType,KeyType);
    if(H.tag==1)    prior=greatPrior;
    else    prior=lessPrior;
    while(1!=curr&&prior(H.rcd[curr].key,H.rcd[curr/2].key)){
        swapHeapElem(H,curr,curr/2);
        curr/=2;
    }
    return OK;
}

//删除堆H的堆顶结点,并用e将其返回
Status RemoveFirstHeap(Heap &H,RcdType &e){
    if(H.n<=0) return ERROR;
    e=H.rcd[1];
    swapHeapElem(H,1,H.n);
    H.n--;            //交换堆顶与堆尾结点,堆长度减1
    if(H.n>1) ShiftDown(H,1);//从对顶位置向下筛选
    return OK;
}

//删除位置pos的结点,用e返回其值
Status RemoveHeap(Heap &H,int pos,RcdType &e){
    if(H.n<=0||pos<=0||pos>H.n) return ERROR;
    e=H.rcd[pos];
    swapHeapElem(H,pos,H.n);
    H.n--;
    if(H.n>1) ShiftDown(H,pos);
    return OK;
}

//堆排序
void HeapSort(RcdSqList &L,int tag){
    Heap H;
    int i;
    RcdType e;
    MakeHeap(H,L.rcd,L.length,L.size,tag);

    for(i=H.n;i>0;i--){
        RemoveFirstHeap(H,e);
    }
}

//求深度
int Depth(Heap H){
    float depth=log(H.n)/log(2)+1;
    return (int)depth;
}

//打印堆的记录
void printHeap(Heap H){
    if(H.rcd==NULL){
        printf("此堆为空\n");
        return;
    }
    int i,j;
    printf("顺序存储结构:\n");
    for(i=1;i<=H.n;i++){
        printf("%d ",H.rcd[i].key);
    }
    printf("\n\n树形:\n");
    for(i=1,j=1;i<=H.n;i++){
        printf("%d ",H.rcd[i].key);
        if(i==(int)pow(2,j)-1){
            printf("\n");
            j++;
        }
    }
    printf("\n深度为%d\n\n",(Depth(H)));
}



int main(){
    Heap H;         //目标堆
    int size=40,i,n;
    RcdSqList L;                //存用来做堆排序的记录
    RcdType E[40];                 //创建堆等操作专用
    RcdType R[40];                 //堆排序专用

    printf("\n----------开始----------\n\n\n");
    printf("你可以进行下面的操作:\n");
    printf("1、初始化一个空堆H\n");
    printf("2、创建一个有记录的堆H\n");
    printf("3、删除堆H顶结点\n");
    printf("4、删除堆H指定的pos位置的结点\n");
    printf("5、交换堆H中的第i结点和第j结点\n");
    printf("6、销毁堆H\n");
    printf("7、将e插入堆H\n");
    printf("8、对位置pos结点做筛选\n");
    printf("9、打印堆H\n");
    printf("10、堆排序\n");
    printf("11、测试大顶堆函数\n");
    printf("12、测试小顶堆函数\n");
    printf("13、退出\n\n");

    int choice=0;
    int tag,pos,t1,t2;
    KeyType x,y;
    RcdType a;
    while(choice!=13){
        printf("请输入你的选择:\n");
        scanf("%d",&choice);
        switch(choice){
            case 1:
                    //测试初始化函数   Status InitHeap(Heap &H,int size,int tag)
                    printf("请选择  1、大顶堆 0、小顶堆\n");
                    scanf("%d",&tag);
                    if(tag!=0&&tag!=1) {
                        printf("输入数据有误,请重新选择\n");
                        break;
                    }
                    if(InitHeap(H,size,tag))
                        printf("初始化成功\n");
                    else
                        printf("初始化失败\n");
                    printf("你创建的堆为:\n\n");
                    printHeap(H);
                    break;


            case 2:
                    //建长度为n的堆H,容量为size,当tag为0或1时分别表示小顶堆和大顶堆
                    printf("请选择  0、小顶堆  1、大顶堆\n");
                    scanf("%d",&tag);
                    if(tag!=0&&tag!=1) {
                        printf("输入数据有误,请重新选择\n");
                        break;
                    }
                    printf("你想创建一个对长度为多少的堆\n");
                    printf("输入后按回车(小于%d)\n\n",size);
                    printf("堆长度 n=");
                    scanf("%d",&n);
                    if(n>=size||n<0){
                        printf("数据不合法,请重新选择再输入\n");
                        break;
                    }
                    if(n==0){
                        InitHeap(H,size,tag);
                        printf("你创建的堆为:\n\n");
                        printHeap(H);
                        break;
                    }
                    printf("\n请输入%d个数\n",n);
                    for(i=1;i<=n;i++){
                        printf("第%d个记录的关键字:",i);
                        scanf("%d",&E[i].key);
                    }
                    printf("\n----------输入结束----------\n\n");
                    printf("你创建的堆为:\n\n");
                    MakeHeap(H,E,n,size,tag);
                    printHeap(H);
                    break;


            case 3:
                    //测试删除堆顶结点的函数   Status RemoveFirstHeap(Heap &H,RcdType &e)
                    if(RemoveFirstHeap(H,a)){
                        printf("堆顶结点记录为:%d\n",a.key);
                        printf("删除堆顶后的堆是:\n");
                        printHeap(H);

                    }else{
                        printf("删除失败,此堆为空堆,请重新选择\n");
                    }
                    break;

            case 4:
                    //测试删除pos位置结点的函数   Status RemoveHeap(Heap &H,int pos,RcdType &e)
                    printf("你要删除结点的位置是pos=");
                    scanf("%d",&pos);
                    if(RemoveHeap(H,pos,a)){
                    printf("你删除了%d位置结点记录为:%d\n",pos,a.key);
                    printf("删除%d位置结点后的堆是:\n",pos);
                    printHeap(H);
                    }else{
                        printf("删除失败,数据有误,请重新选择\n");
                    }
                    break;


            case 5:
                    //测试交换函数   Status swapHeapElem(Heap &H,int i,int j)
                    printf("请输入你想交换的两个结点的位置:(中间用逗号[,]隔开)\n");
                    scanf("%d,%d",&t1,&t2);
                    if(swapHeapElem(H,t1,t2)){
                        printf("交换成功\n");
                        printHeap(H);
                    }
                    else
                    printf("交换失败,请重新选择\n");
                    break;


            case 6:
                    //测试销毁函数
                    DestroyHeap(H);
                    printf("摧毁成功\n");
                    break;


            case 7:
                    //测试插入函数    Status InsertHeap(Heap &H,RcdType e)
                    printf("请输入你要插入的记录关键字RcdType a=");
                    scanf("%d",&a.key);
                    if(InsertHeap(H,a)){
                        printf("插入后的堆\n");
                        printHeap(H);
                    }
                    else printf("插入失败,堆已满或者已摧毁,请重新选择\n");
                    break;



            case 8:
                    //对堆H中位置为pos的结点做筛选,将以pos为根的子树调整为子堆
                    printf("请输入你要做筛选的位置pos=");
                    scanf("%d",&pos);
                    if(pos<=0||pos>H.n) {
                        printf("输入数据有误,请重新选择\n");
                        break;
                    }
                    ShiftDown(H,pos);
                    printf("筛选成功\n");
                    printHeap(H);
                    break;


            case 9:
                    printHeap(H);
                    break;


            case 10:
                    //测试堆排序函数   void HeapSort(RcdSqList &L)
                    printf("请选择  0、降序 1、升序\n");
                    scanf("%d",&tag);
                    if(tag!=0&&tag!=1) {
                        printf("输入数据有误,请重新选择\n");
                        break;
                    }
                    printf("你要排序的记录有多少个?\n");
                    scanf("%d",&L.length);
                    if(L.length>=size){printf("输入数据有误,请重新选择\n");break;}
                    L.size=size;
                    for(i=1;i<=L.length;i++){
                        printf("第%d个记录关键字:",i);
                        scanf("%d",&R[i].key);
                    }
                    L.rcd=R;
                    HeapSort(L,tag);
                    printf("堆排序结果\n");
                    for(i=1;i<=L.length;i++){
                        printf("%d ",L.rcd[i].key);
                    }
                    printf("\n");
                    break;


            case 11:
                    //测试大顶堆优先函数int greatPrior(KeyType x,KeyType y)
                    printf("你要测试的两个输请输入\n");
                    printf("第1个数:");
                    scanf("%d",&x);
                    printf("第2个数:");
                    scanf("%d",&y);
                    if(greatPrior(x,y)){
                        printf("%d优先于%d\n",x,y);
                    }else{
                        printf("%d优先于%d\n",y,x);
                    }
                    break;

            case 12:
                    //测试小顶堆优先函数int lessPrior(KeyType x,KeyType y)
                    printf("你要测试的两个输请输入\n");
                    printf("第1个数:");
                    scanf("%d",&x);
                    printf("第2个数:");
                    scanf("%d",&y);
                    if(lessPrior(x,y)){
                        printf("%d优先于%d\n",x,y);
                    }else{
                        printf("%d优先于%d\n",y,x);
                    }
                    break;

            case 13:
                    printf("你选择了退出,感谢使用\n");
                    break;


            case 14:
                    printf("你可以进行下面的操作:\n");
                    printf("1、初始化一个空堆H\n");
                    printf("2、创建一个有记录的堆H\n");
                    printf("3、删除堆H顶结点\n");
                    printf("4、删除堆H指定的pos位置的结点\n");
                    printf("5、交换堆H中的第i结点和第j结点\n");
                    printf("6、销毁堆H\n");
                    printf("7、将e插入堆H\n");
                    printf("8、对位置pos结点做筛选\n");
                    printf("9、打印堆H\n");
                    printf("10、堆排序\n");
                    printf("11、测试大顶堆函数\n");
                    printf("12、测试小顶堆函数\n");
                    printf("13、退出\n\n");
                    break;


            default:printf("输入错误,请输入1-14范围内的数字\n");break;

        }
        printf("----------分割线(输入14可以弹出选项)-----------\n\n");
    }


}



5.调试分析

调试过程所遇到的问题和解决的方法:

(1) anyview平台上,不支持结构体的函数指针的引用,所以不能直接采用书本上的堆的类型定义。

解决方法:因为堆中H.tag有记录到堆H是大顶堆还是小顶堆,所以对于有限函数的引用问题,可以根据具体的H.tag的值来决定用greatPrior还是lessPrior函数。

(2) 如需输入的一些操作,输入的数据是有限制的,有时候输错会导致程序运行不下去。

解决方法:在用户输入后加入一些if等判断语句来判断数据是否合法,不合法就重新选择操作。

 

6.运行测试

提示:要先程序中选择对应的操作才能进行下面的数据测试

1)基础操作1的测试:

 1组数据:输入1

 2组数据:输入0

 

2)基础操作2的测试:

1组数据:先输入3

 2组数据:先输入1,再输入大于等于size的值(如40)

3组数据:先输入0,再输入0

4组数据:先输入1,再输入3,再输入6个数:42,58,68

5组数据:先输入0,再输入3,再输入6个数:42,58,68

      6组数据:先输入1,再输入6,再输入6个数:42,58,68,98,86,43第7组数据:先输入0,再输入6,再输入6个数:42,58,68,98,86,43

 

3)基础操作3的测试:

1组数据:先使用操作1创建空堆H,再选择3操作

2组数据:先使用操作2创建堆H,再选择3操作

 

4)基础操作4的测试:

1组数据:先使用操作1创建空堆H,再选择4操作,输入2

2组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择4操作,输入7

3组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择4操作,输入5

4组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择4操作,输入1

 

5)基础操作5的测试:

1组数据:先使用操作1创建空堆H,再选择5操作,输入1,2

2组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择5操作,输入0,1

3组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择5操作,输入1,7

4组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择5操作,输入1,6

5组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择5操作,输入1,1

 

6)基础操作6的测试:

1组数据:先使用操作1创建空堆H,再选择6操作

2组数据:先使用操作2创建堆H,再选择6操作

 

7)基础操作7的测试:

1组数据:先使用操作1创建空堆H,再选择7操作,输入22

2组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择7操作,输入22

3组数据:先使用操作2创建堆H,其中堆H已满(例如H.n=39),再选择7操作,输入22

4组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择6操作摧毁,再选择操作7,输入22

 

8)基础操作8的测试:

1组数据:先使用操作1创建空堆H,再选择8操作,输入2

2组数据:先使用操作1创建空堆H,再选择8操作,输入-1

3组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择8操作,输入0

4组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择8操作,输入3

5组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择8操作,输入7

 

9)基础操作9的测试:

1组数据:先使用操作1创建空堆H,再选择9操作

2组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择9操作

3组数据:先使用操作2创建堆H,其中堆H有6个元素,再选择6操作,再选择9操作

 

10)基础操作10的测试:

1组数据:输入2

2组数据:输入0,输入0

3组数据:输入0,输入6,再输入6个数:42,58,68,98,86,43

4组数据:输入1,输入6,再输入6个数:42,58,68,98,86,43

5组数据:输入0,输入40(大于size)

6组数据:输入1,输入40(大于size)

 

11)基础操作11的测试:

1组数据:输入3,5

2组数据:输入40,60

 

12)基础操作12的测试:

1组数据:输入3,5

2组数据:输入40,60

 

7. 存储结构的比较

基础操作

时间复杂度

InitHeap()

O(1)

MakeHeap()

O(n)

RemoveFirstHeap()

O(logn)

RemoveHeap()

O(logn)

swapHeapElem()

O(1)

DestroyHeap()

O(1)

InsertHeap()

O(logn)

ShiftDown()

O(logn)

printHeap()

O(n)

HeapSort()

O(nlogn)

greatPrior()

O(1)

lessPrior()

O(1)

 

8.思考与小结

1)有几个操作如打印,插入等都需要判断堆H是否为空再采取相应的操作。

2)堆排序是利用堆的特性来进行排序的,时间复杂度是O(nlogn)大顶堆排得到的是升序序列,小顶堆排得到的是降序序列。堆排序是不稳定的排序方法。

3)堆是一种很实用的数据结构。

4)交互界面要弄得简洁分明一点。


  • 6
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值