代码写的习惯了,也已经不用查就知道编译器报的错是什么意思了。本次练习,采用栈和队列共同实现二叉树。栈用来保存遍历时未访问的节点,队列用来遍历队列。不得不说,栈和队列的应用范围还是非常广泛的。
话不多说,代码附上。
略微解释:部分代码的判断条件是根据练习题要求来写的,可能不会适配所有条件。当看代码的时候觉得不对劲的时候,就是我擅自改动的地方。具体来说,应该在二叉树的平衡算法当中还不够完善,也就是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;
}