数据结构——平衡二叉树

代码写的习惯了,也已经不用查就知道编译器报的错是什么意思了。本次练习,采用栈和队列共同实现二叉树。栈用来保存遍历时未访问的节点,队列用来遍历队列。不得不说,栈和队列的应用范围还是非常广泛的。

话不多说,代码附上。

略微解释:部分代码的判断条件是根据练习题要求来写的,可能不会适配所有条件。当看代码的时候觉得不对劲的时候,就是我擅自改动的地方。具体来说,应该在二叉树的平衡算法当中还不够完善,也就是balance函数。

再多说一嘴,calloc比malloc好用多了,适用于结构体中有变量的情况来使用。

#include <stdio.h>
#include <stdlib.h>
#define size 20
//要求:对于给定的整数序列,构造平衡二叉查找树,并分层遍历构成的二叉查找树,关键字间以空格分隔 
//单个二叉树节点类 
typedef struct node{
    int num;
    int label;
    int h;
    struct node*l;
    struct node*r;
    struct node*parent;
}node;

//队列类,用于遍历二叉树
typedef struct queue{
    node *a[size];
    int front;
    int rear;
}queue;

//栈的子节点
typedef struct list{
    node *data;
    struct list *next;
}list;
 
//栈类
typedef struct stack{
    list *top;
    list *bottom;
}stack;
 
//初始化队列
queue *initq(queue*q){
    q->front=q->rear=0;
    int i=0;
    for(i=0;i<size;i++){
        q->a[i]=calloc(1,sizeof(node));
        q->a[i]=0;
    }
    return q;

//入队列
void inq(queue*q,node*p){
    if((q->rear+1)%size==q->front)printf("队列已满");
    q->a[q->rear]=p;
    q->rear=(q->rear+1)%size;

//出队列
node *outq(queue*q){
    if(q->front==q->rear)printf("队列已空");
    node*p;
    p=calloc(1,sizeof(node));
    p->l=calloc(1,sizeof(node));
    p->r=calloc(1,sizeof(node));
    p->l=p->r=0;
    p=q->a[q->front];
    q->front=(q->front+1)%size;
    return p;

//初始化栈
void inits(stack*s){
    s->top=s->bottom;
    s->bottom->next=NULL;
    //printf("%d",s->bottom->next);

//入栈
void push(stack*s,node*t){
    list*p;
    p=calloc(1,sizeof(list));
    p->data=t;
    p->next=s->top;
    s->top=p;

//出栈
node *pop(stack*s){
    if(s->top==0)printf("栈空");
    node*p;
    p=calloc(1,sizeof(node));
    p=s->top->data;
    s->top=s->top->next;
    return p;

//标注平衡因子
void label(node*t,stack*s){
    while(1){
        if(t->r!=0&&t->l!=0){
            t->label=t->l->h-t->r->h;
        }else if(t->r!=0&&t->l==0){
            t->label=0-t->r->h;
        }else if(t->r==0&&t->l!=0){
            t->label=t->l->h;
        }else if(t->r==0&&t->l==0){
            t->label=0;
        }
        if(t->r!=0){//压右走左 
            push(s,t->r);            
        }
        if(t->l!=0){
            t=t->l;
        }else break;
    }//出栈 
    while(s->top->data!=0){
        node*p;
        p=calloc(1,sizeof(node));
        p=pop(s);
        while(1){
            if(p->r!=0&&p->l!=0){
                p->label=p->l->h-p->r->h;
            }else if(p->r!=0&&p->l==0){
                p->label=0-p->r->h;
            }else if(p->r==0&&p->l!=0){
                p->label=p->l->h;
            }else if(p->r==0&&p->l==0){
                p->label=0;
            }
            if(p->r!=0){//压右走左 
                push(s,p->r);            
            }
            if(p->l!=0){
                p=p->l;
            }else break;    
        }
    }

//构造平衡二叉树,构造应该是循环执行。执行完函数,各个节点的深度都已经标注完,而且会自动标注平衡因子 
void create(node*t,int e,stack*s){
    //这边缺了平衡旋转法的代码 
    int cnt=0;
    //头节点的判定 
    if(t->num==0){
        t->num=e;
        t->h=1;
    }
    else{
        node*p;
        p=calloc(1,sizeof(node));
        p->l=(node*)malloc(sizeof(node));
        p->r=(node*)malloc(sizeof(node));
        p->parent=(node*)malloc(sizeof(node));
        p->l=NULL;p->r=NULL;p->parent=NULL;
        p->h=1;
        p->num=e;
        while(1){
            //e大于节点值, 且右子树不为空,向右走 
            if(e>t->num&&t->r!=0){
                t=t->r;
            }else if(e>t->num&&t->r==0){//e大于节点值且右子树为空 
                t->r=p;
                p->parent=t;
                p->h=1;
                break;
            }else if(e<t->num&&t->l!=0){//e小于节点且左子树不为空 
                t=t->l;
            }else{//e小于节点且左子树为空 
                t->l=p;
                p->parent=t;
                p->h=1;
                break;
            }
        }
    }
}

//单独写平衡二叉树的函数,插入之后执行,balance写在walk之后,还要再平衡完之后更改头节点 
node *balance(node*a[],node*t,stack*s){
    int i=0;
    node*b;//从数组头开始,保证了第一次是头节点操作 
    b=calloc(1,sizeof(node));
    node*p;
    p=calloc(1,sizeof(node));
    while(!(a[i]->label==0||a[i]->label==-1||a[i]->label==1)){
        if(a[i]->label>0){
            //找左子树的最大节点,label>0保证了节点一定有左子树
            b=a[i];
            node*c;
            c=calloc(1,sizeof(node));
            push(s,b->l);
            b=b->l; 
            while(1){
                if(b->r!=NULL){
                    push(s,b->r);
                }
                if(b->l!=NULL){
                    b=b->l;
                }
                else break;
            }
            b=pop(s);
            while(s->top->data!=NULL){
                c=pop(s);
                if(c->num>b->num)b=c;
                else continue;
            }
            //b是最大节点
            if(b->parent==a[i]->l){
                //LR型的旋转
                c=a[i]->l;
                b->l=c;
                c->r=NULL;
                b->parent=a[i]->parent;
                a[i]->parent=b;
                b->r=a[i];
                a[i]->l=NULL;
                break;
            }
            b->parent=a[i]->parent;
            a[i]->parent=b;
            b->r=a[i];
            a[i]->l=NULL;
            break;
        }else{
            //找右子树的最小节点 
            p=a[i];
            b=a[i];
            if(a[i]->r->label<-1){
                p=p->r;
                b=p->r->l;
                node*c;
                c=calloc(1,sizeof(node));
                c=p->r;
                b->parent=a[i];
                p->parent=b;
                c->parent=b;
                b->r=c;
                c->l=NULL;
                b->l=p;
                p->r=NULL;
                a[i]->r=b;
                break;
            }
            node*c;
            c=calloc(1,sizeof(node));
            push(s,b->r);
            b=b->r;
            while(1){
                if(b->l!=NULL){
                    push(s,b->l);
                }
                if(b->r!=NULL){
                    b=b->r;
                }
                break;
            }
            b=pop(s);
            while(s->top->data!=NULL){
                c=pop(s);
                if(c->num<b->num)b=c;
                else continue;
            }
            c=a[i]->r;
            b->parent=a[i]->parent;
            a[i]->parent=b;
            b->l=a[i];
            a[i]->r=NULL;
            break;
        }
    }    
    while(t->parent!=NULL){
        t=t->parent;
    }
    return t;

//遍历函数,得到节点列表 
void walk(node*t,queue*q,node*a[]){
    int i=0;
    inq(q,t);
    node *b;
    b=(node*)malloc(sizeof(node));
    while(q->front!=q->rear){
        b=outq(q);
        a[i]=b;
        i++;
        if(b->l!=0){
            inq(q,b->l);
        }
        if(b->r!=0){
            inq(q,b->r);
        }
    }

//求结构体数组的长度
int sizea(node*a[]){
    int i=0;
    while(a[i]!=NULL){
        i++;
    }
    return i;

//回溯函数,用于标注深度,头节点的深度不重要,漏掉也行 
void back(node*t,stack*s,node*a[],queue*q){
    node*p;
    p=calloc(1,sizeof(node));
    int i=0;
    int j=0;
    int b=0;
    int cnt=1;
    walk(t,q,a);
    b=sizea(a);
    while(j<b){
        i=j;
        if(a[i]->l==NULL&&a[i]->r==NULL){
            cnt=1;
            p=a[i];
            while(p->parent!=NULL){
                p->h=cnt;
                cnt+=1;
                p=p->parent;
            }
            j++;
        }else{
            j++;
        }
    }

int main(int argc,char *argv[]){
    node*t;
    t=calloc(1,sizeof(node));
    t->l=(node*)malloc(sizeof(node));
    t->r=(node*)malloc(sizeof(node));
    t->parent=(node*)malloc(sizeof(node));
    t->parent=NULL;t->l=NULL;t->r=NULL;
    t->h=1;
    //t=NULL;t->l=NULL;t->r=NULL;t->parent=NULL;
    node*a[10];
    int i=0;
    for(i=0;i<10;i++){
        a[i]=calloc(1,sizeof(node));
        a[i]=0;
    }
    int e=0;
    queue*q;
    q=calloc(1,sizeof(queue));
    q=initq(q);
    stack*s;
    s=(stack*)malloc(sizeof(stack));
    s->top=calloc(1,sizeof(list));
    s->bottom=calloc(1,sizeof(list));
    inits(s);
    //以上代码为初始化 
    for(i=0;i<5;i++){
        scanf("%d",&e);
        create(t,e,s);
        back(t,s,a,q);
        label(t,s);
        walk(t,q,a);
        t=balance(a,t,s);
        back(t,s,a,q);
        label(t,s);
    }
    walk(t,q,a);
    int b=0;
    b=sizea(a);
    for(i=0;i<b;i++){
        printf("%d ",a[i]->num);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值