一、建立按照层次编号的二叉树
//按照层次编号的二叉树
/*
1
2 3
4 5 6 7
8 9 10 11 12 13 14 15
其中 1的子树是2和3
2的子树是4和5,3的子树是6和7
以此类推
*/
//按照层次编号的二叉树,在遍历和构造时要善于利用队列这一数据结构
#include <stdio.h>
#include <stdlib.h>
#define QUEUENUM 1000
typedef struct node
{
int id;
//编号
struct node* left;
//指向左孩子的指针
struct node* right;
//指向右孩子的指针
struct node* father;
//指向父亲的指针
}Tree;
void ini_tree(Tree** proot,int n);
//用于构建树,第一个参数是指向被构建树的根指针的指针,第二个参数是被构建树的结点个数
void read_tree(Tree* root);
//用于按照层次遍历树,参数是被遍历树的根指针
int main(void)
{
int n;
scanf("%d",&n);
//n用来记录结点的个数
Tree* root=NULL;
//root为根指针
ini_tree(&root,n);
read_tree(root);
return 0;
}
void ini_tree(Tree** proot,int n)
{
Tree **queue;
Tree **queue_father;
queue=(Tree**)calloc(QUEUENUM,sizeof(Tree*));
queue_father=(Tree**)calloc(QUEUENUM,sizeof(Tree*));
int front=-1;
int rear=-1;
//以上代码块构建了一个复合队列
//队列存储的信息是,下一个结点应该构建在哪里?以及,被构建结点的父亲是谁?
//queue[i]的值是某个待构建结点的地址
//queue_father[i]的值是 这个待构建结点父亲的地址
Tree* c;
//用于被calloc的创建结点打工人
int i;
for (i=1;i<=n;i++)
{
if (*proot==NULL)
{
c=(Tree*)calloc(1,sizeof(Tree));
c->id=i;
c->left=NULL;
c->right=NULL;
c->father=NULL;
*proot=c;
queue[++rear]=c->left;
queue_father[rear]=c;
queue[++rear]=c->right;
queue_father[rear]=c;
//构建结点的同时,把可以构建结点的地址按顺序压入队列中
}
else
{
++front;
c=(Tree*)calloc(1,sizeof(Tree));
c->id=i;
c->left=NULL;
c->right=NULL;
c->father=queue_father[front];
//注意,父亲结点的左右孩子地址需要被赋值
//一个父亲要先有左孩子,再右孩子
//如果左孩子的地址为空,那么这个父亲就没有左孩子
//我们就把新建立的结点分给这个父亲当他的左孩子
if (c->father->left==NULL)
{
c->father->left=c;
}
//如果左孩子的地址为空,那么这个父亲就有左孩子
//但按照算法的流程我们知道,这个父亲肯定没有右孩子
//我们就把新建立的结点分给这个父亲当他的右孩子
else
{
c->father->right=c;
}
queue[++rear]=c->left;
queue_father[rear]=c;
queue[++rear]=c->right;
queue_father[rear]=c;
//构建结点的同时,把可以构建结点的地址按顺序压入队列中
}
}
free(queue);
free(queue_father);
}
void read_tree(Tree* root)
{
Tree **queue;
queue=(Tree**)calloc(QUEUENUM,sizeof(Tree*));
int front=-1;
int rear=-1;
//以上代码块构建了一个队列
//队列存储的信息是,下一个遍历的结点在哪里?
//queue[front]的值是下一个待遍历结点的地址
Tree* p=root;
printf("id=%d\n",p->id);
if (p->left!=NULL)
queue[++rear]=p->left;
if (p->right!=NULL)
queue[++rear]=p->right;
while(front<rear)
{
p=queue[++front];
printf("id=%d\n",p->id);
if (p->left!=NULL)
queue[++rear]=p->left;
if (p->right!=NULL)
queue[++rear]=p->right;
}
free(queue);
}
注意:
1、为什么我在函数ini_tree和read_tree中要采用calloc的方式构建队列数组?
因为c语言在子函数中所能直接建立数组的大小是有限的。我们构建队列时采取了最为简单的非循环顺序储存结构,需要大量的空间。所以需要用calloc在申请空间
2、为什么ini_tree的参数是二重指针,而read_tree的参数是一重指针?
因为ini_tree函数需要对root指针的值进行改变,且作用于全局。root是一个一重指针,在函数中需要借助二重指针进行赋值。
二、构建查找二叉树——非递归实现
#include <stdio.h>
#include <stdlib.h>
#define NUM 100
#define QUEUENUM 1000
typedef struct node
{
int num;
int count;
//数据
struct node* left;
//指向左孩子的指针
struct node* right;
//指向右孩子的指针
struct node* father;
//指向父亲的指针
}Tree;
Tree* stack[NUM];
//一个指针栈,存储着当前遍历结点到根结点的所有指针
//其中,stack[0]恒为根结点的指针,以此类推
int top=-1;
void read_tree(Tree* root);
//用于按照层次遍历树,参数是被遍历树的根指针
void build_tree(Tree** proot,int num);
//用于构建查找树,每次处理一个数据
int main(void)
{
int n;
scanf("%d",&n);
Tree* root;
int i;
int num;
for (i=1;i<=n;i++)
{
scanf("%d",&num);
build_tree(&root,num);
}
read_tree(root);
return 0;
}
void build_tree(Tree** proot,int num)
{
Tree* c;
//用于被calloc的创建结点打工人
Tree* tmp;
//用于寄存地址的一个指针
top=-1;
//初始化栈
if (*proot==NULL)
{
c=(Tree*)calloc(1,sizeof(Tree));
c->left=NULL;
c->right=NULL;
c->num=num;
c->count=1;
*proot=c;
//如果头指针为空,不用考虑那么多,直接建立根结点
}
else
{
stack[++top]=*proot;
//如果头指针不空,那么就按照正常流程进行,先把头指针压入栈中
while(1)
{
//遍历到任何一个结点时,要考虑待进树的数据和当前结点数据的大小
//如果当前结点数据和待进树的数据相等,直接进树,解决战斗,退出循环
if (num==stack[top]->num)
{
(stack[top]->count)++;
break;
}
//如果进树的数据小于当前结点数据,肯定是去左边,那就要看看左子树是不是空了
else if (num<stack[top]->num)
{
//左子树为空,直接创建左子树,然后完成任务,退出循环
if (stack[top]->left==NULL)
{
c=(Tree*)calloc(1,sizeof(Tree));
c->left=NULL;
c->right=NULL;
c->num=num;
c->count=1;
stack[top]->left=c;
break;
}
//否则,往下继续探索
//注意,这时候要把下一个要探索的结点的地址压入栈中
else
{
tmp=stack[top]->left;
stack[++top]=tmp;
continue;
}
}
//否则,那么进树的数据就大于当前结点数据,要去右边了,同样要看看右子树是不是空了
else
{
//右子树为空,直接创建右子树,然后完成任务,退出循环
if (stack[top]->right==NULL)
{
c=(Tree*)calloc(1,sizeof(Tree));
c->left=NULL;
c->right=NULL;
c->num=num;
c->count=1;
stack[top]->right=c;
break;
}
//否则,往下继续探索
//注意,这时候要把下一个要探索的结点的地址压入栈中
else
{
tmp=stack[top]->right;
stack[++top]=tmp;
continue;
}
}
}
}
}
//这是第一个示例程序中写的按层次遍历的代码,这里直接拿来用了
//printf语句改了一下
void read_tree(Tree* root)
{
Tree **queue;
queue=(Tree**)calloc(QUEUENUM,sizeof(Tree*));
int front=-1;
int rear=-1;
Tree* p=root;
printf("num=%d count=%d\n",p->num,p->count);
if (p->left!=NULL)
queue[++rear]=p->left;
if (p->right!=NULL)
queue[++rear]=p->right;
while(front<rear)
{
p=queue[++front];
printf("num=%d count=%d\n",p->num,p->count);
if (p->left!=NULL)
queue[++rear]=p->left;
if (p->right!=NULL)
queue[++rear]=p->right;
}
free(queue);
}
三、二叉树的遍历——非递归实现
#include <stdio.h>
#include <stdlib.h>
#define NUM 100
typedef struct node
{
int num;
int count;
struct node* left;
struct node* right;
struct node* father;
}Tree;
Tree* stack[NUM];
int isread_stack[NUM];
int top=-1;
void build_tree(Tree** proot,int num);
void read_tree1(Tree* root);
int main(void)
{
int n;
scanf("%d",&n);
Tree* root;
int i;
int num;
for (i=1;i<=n;i++)
{
scanf("%d",&num);
build_tree(&root,num);
}
read_tree1(root);
return 0;
}
void build_tree(Tree** proot,int num)
{
Tree* c;
Tree* tmp;
top=-1;
if (*proot==NULL)
{
c=(Tree*)calloc(1,sizeof(Tree));
c->left=NULL;
c->right=NULL;
c->num=num;
c->count=1;
*proot=c;
}
else
{
stack[++top]=*proot;
while(1)
{
if (num==stack[top]->num)
{
(stack[top]->count)++;
break;
}
else if (num<stack[top]->num)
{
if (stack[top]->left==NULL)
{
c=(Tree*)calloc(1,sizeof(Tree));
c->left=NULL;
c->right=NULL;
c->num=num;
c->count=1;
stack[top]->left=c;
break;
}
else
{
tmp=stack[top]->left;
stack[++top]=tmp;
continue;
}
}
else
{
if (stack[top]->right==NULL)
{
c=(Tree*)calloc(1,sizeof(Tree));
c->left=NULL;
c->right=NULL;
c->num=num;
c->count=1;
stack[top]->right=c;
break;
}
else
{
tmp=stack[top]->right;
stack[++top]=tmp;
continue;
}
}
}
}
}
void read_tree1(Tree* root)
{
top=-1;
stack[++top]=root;
isread_stack[top]=0;
//栈初始化,把根结点的地址和被访问状态信息压入两个栈中
Tree* tmp;
//一个临时变量指针,在这里显得突兀,其实不重要,先别管它
while(1)
{
//如果是第一次到达
if (isread_stack[top]==0)
{
printf("num=%d count=%d\n",stack[top]->num,stack[top]->count);
isread_stack[top]=1;
//遍历左子树
if (stack[top]->left!=NULL)
{
tmp=stack[top]->left;
stack[++top]=tmp;
continue;
}
}
//如果是第二次到达
else if (isread_stack[top]==1)
{
isread_stack[top]=2;
//遍历右子树
if (stack[top]->right!=NULL)
{
tmp=stack[top]->right;
stack[++top]=tmp;
continue;
}
}
//如果是第三次到达
else if (isread_stack[top]==2&&top!=0)
{
isread_stack[top]=0;
top--;
}
//如果是第三次到达且这个结点是根结点
//意味着树遍历完了,函数该结束了,退出循环吧
else if (isread_stack[top]==2&&top==0)
{
break;
}
}
}