二叉树
1.完全二叉树
-
定义:如果一个二叉树扣除最下面一层节点后成为一个满二叉树,并且被扣除的节点都向左靠齐,那么这个二叉树是完全二叉树
-
性质:
- 叶子节点一定只出现在最下面一层
- 最下面一层叶子节点一定集中在左边连续位置
- 倒数第二层,如果有叶子节点,一定出现在右边连续位置
- 节点数一致,完全二叉树深度最小
- 对于一个具有n个节点的完全二叉树,如果按照从上到下,同一层的节点按从左到右的顺序对二叉树中所有节点从1开始编号,对于序号为i的节点,有:
- 如果i>1,则序号为i的节点的双亲节点的序号为不大于(i/2)的最大整数
- 如果2i>n,则第i个节点无左子女(终端节点),否则,其左子女节点为2i
- 如果2i+1>n,则i节点无右子女,否则其右子女节点为2i+1
-
完全二叉树的顺序存储
- 根据性质5,可以把一个二叉树按性质5排序后存放在一个一维数组中,这样无需做其他任何操作,即可完成二叉树的存储,查找
/***************************************************************
*** 完全二叉树的顺序存储
****************************************************************/
#define MAXSIZE 20
typedef char datatype; //树的节点类型
//定义一维数组用来存储树
datatype tree[MAXSIZE];
int n; //树中实际包含的节点个数
- 一般二叉树的顺序存储
一般二叉树由于不具备性质5,下标无法体现节点之间的关系,所以根据二叉树的性质,每个节点最多只有两个子节点,所以在存储二叉树节点时,可以添加两个域,分别用来存储两个子节点的下标
/***************************************************************
*** 普通二叉树的顺序存储
****************************************************************/
typedef struct
{
datatype info[MAXSIZE];
int lchild, rchild;
}node;
int n; /*树中实际节点个数*/
int root; /*用来存放根节点下标*/
有时,不但需要从父母访问子女,还需要从子女节点访问到父母,只需要在每个节点上加上父母的下标即可
/***************************************************************
*** 普通二叉树的顺序存储
****************************************************************/
typedef struct
{
datatype info[MAXSIZE];
int lchild, rchild;
int parent; /*用来存放父母节点下标*/
}node;
int n; /*树中实际节点个数*/
int root; /*用来存放根节点下标*/
链式存储
二叉树的顺序存储虽然简单,但必须预先给出数组大小,容易发生数组越界,所以链式存储更加适合用来存储二叉树
链式存储每个节点同样包含三个域,分别是左右孩子域以及值域,不同于顺序存储的是,左右孩子域存储子节点的方式不再是存储下标,而是通过指针实现
/***************************************************************
*** 普通二叉树的链式存储
****************************************************************/
typedef struct treeNode
{
datatype info;
treeNode * lchild, * rchild;
treeNode * parent;
}bintNode;
bintNode *root1; /*指向根节点的指针*/
二叉树的遍历
1.前序遍历
遍历顺序为 根->左->右
- 前序遍历的递归实现
/*前序遍历*/
void preorder(bintNode * t)
{
if (t)
{
printf("%c", t->info);
preorder(t->lchild);
preorder(t->rchild);
}
}
```c
* 前序遍历非递归实现
递归算法效率低,所以一般使用非递归算法,使用非递归遍历时,必须用一个栈来记录回溯点,以便将来回溯
```c
/*定义栈,用来实现非递归回溯*/
typedef struct
{
bintNode * data[100];
int top; /*栈顶指针*/
int tag[100];
}seqstack;
void push(seqstack * p,bintNode * t) /*入栈*/
{
p->data[p->top] = t;
p->top++;
}
bintNode * pop(seqstack * p)
{
if (p->top != 0) /*如果栈不为空*/
{
p->top--;
return p->data[p->top]; /*返回栈顶元素*/
}
else
{
return NULL;
}
根据二叉树前序遍历的定义,程序先访问根节点,在访问左子树,最后访问右子树,比如图一,前序遍历的结果应该是1,2,4,8,9,5,10,3,6,7
在访问完根节点1后,进入由2,4,5,8,9,10组成的左子树,在这一棵子树上,依旧遵循前序遍历的规则,先访问根节点2,然后访问2的左子树,同样,4是2的左子树的根节点,访问玩根4后访问左子树8,8没有后继节点,返回访问右子树9,在这之后需要返回2去访问2的右子树,为了能够返回去,需要设立回溯点,在访问完1后,把1放在栈中,再去访问2,2访问完后把2再放在栈中,4也一样,这样在访问完8后发现没有后继节点,就从栈中取出栈顶元素,由于栈的先入后出性,取出的栈顶元素就是最后放进去的元素4,取出4后访问他的右节点,访问完后再从栈中取出栈顶元素,也就是2,依次便可访问完整个二叉树
void preorder1(bintNode * s)
{
seqstack seq;
seq.top = 0; /*初始化栈*/
while (seq.top != 0 || (s))
{
if (s)
{
printf("%c", s->info);
push(&seq, s);
s = s->lchild;
}
else
{
s=pop(&seq);
s = s->rchild;
}
}
}
2.中序遍历
中序遍历与前序遍历几乎一致,仅仅改变访问先后次序
- 递归实现
/*中序遍历*/
void inorder(bintNode * t)
{
if (t)
{
inorder(t->lchild);
printf("%c", t->info);
inorder(t->rchild);
}
}
- 非递归实现
void inorder1(bintNode * s)
{
seqstack seq;
seq.top = 0; /*初始化栈*/
while (seq.top != 0 || (s))
{
if (s)
{
push(&seq, s);
s = s->lchild;
}
else
{
s = pop(&seq);
printf("%c", s->info);
s = s->rchild;
}
}
}
后序遍历
- 递归实现
/*后序遍历*/
void postorder(bintNode * t)
{
if (t)
{
postorder(t->lchild);
postorder(t->rchild);
printf("%c", t->info);
}
}
- 非递归实现
后序遍历的非递归实现和前序中序有一些不同,应为根据后序遍历的定义,需要先访问左子树,在访问右子树,最后访问根节点,在循环访问左子树的过程中,我们把左子树对应的上一级根节点存放在栈中,当左子树访问完需要访问右子树时,需要回溯上去,但这时根节点还没有被访问,如果让其出栈,根节点就会丢失,这时我们需要在定义栈时怎加一个数组tag用来标记栈中元素的状态,每个元素刚进栈时,tag为0,当他第一次位于栈顶即将被处理时(应该访问他的右子树时),tag=0,将他的右子树作为处理对象,此时,改栈顶元素依然保留在栈中,把tag=1;表示右子树访问完成,然后将其出栈,访问他自己,依次循环完成遍历 - 以图一为例:
按后续遍历规则,最终遍历结果应该是:8,9,4,10,5,2,6,7,3,1
程序运行的过程是:从根节点1开始访问,依次按后续遍历规则访问2,4,8,并把他们的tag=0,8没有后继节点,也就是左右子树访问完成,把tag=1,出栈访问自己,此时栈顶元素为4,tag=0标志这即将访问4的右子树,把二叉树指针指向栈顶元素,访问其右子树,完成之后把tag=1,访问栈顶元素,依次循环完成遍历
/*后序遍历*/
void postorder1(bintNode * s)
{
seqstack seq;
seq.top = 0; /*初始化栈*/
while (seq.top != 0 || (s))
{
if (s)
{
seq.data[seq.top] = s; /*应为需要定义tag,不能使用push*/
seq.tag[seq.top] = 0;
seq.top++;
s = s->lchild;
}
else
{
if (seq.tag[seq.top - 1] == 1)
{
seq.top--;
s = seq.data[seq.top];
printf("%c", s->info);
s = NULL;
}
else
{
s = seq.data[seq.top - 1];
seq.tag[seq.top-1] = 1;
s = s->rchild;
}
}
}
}
二叉树的创建
/***************************************************************
*** 二叉树的创建
****************************************************************/
/*根据前序遍历创建*/
bintNode *createbintree()
{
char ch;
bintNode *t;
if ((ch = getchar()) == '#')
t = NULL;
else
{
t = (bintNode *)malloc(sizeof(bintNode));
t->info = ch;
t->lchild = createbintree();
t->rchild = createbintree();
}
return t;
}
/*根据中序遍历创建*/
bintNode *createbintree()
{
char ch;
bintNode *t;
if ((ch = getchar()) == '#')
t = NULL;
else
{
t = (bintNode *)malloc(sizeof(bintNode));
t->lchild = createbintree();
t->info = ch;
t->rchild = createbintree();
}
return t;
}
完整代码
/***************************************************************
*** 二叉树
*** 1.二叉树的创建
*** 2.二叉树的遍历
*** 3.统计二叉树的节点数
*** 4.求二叉树的高度
*** 5.判断二叉树是否等价
****************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAXSIZE 20
typedef char datatype; //树的节点类型
/***************************************************************
*** 完全二叉树的顺序存储
****************************************************************/
datatype tree[MAXSIZE]; //定义一维数组用来存储树
int n_all; //树中实际包含的节点个数
/***************************************************************
*** 普通二叉树的顺序存储
****************************************************************/
typedef struct
{
datatype info[MAXSIZE];
int lchild, rchild;
int parent; /*用来存放父母节点下标*/
}node;
int n; /*树中实际节点个数*/
int root; /*用来存放根节点下标*/
/***************************************************************
*** 普通二叉树的链式存储
****************************************************************/
typedef struct treeNode
{
datatype info;
struct treeNode * lchild, * rchild;
struct treeNode * parent;
}bintNode;
bintNode * roots; /*指向根节点的指针*/
/*定义栈,用来实现非递归回溯*/
typedef struct
{
bintNode * data[100];
int top; /*栈顶指针*/
int tag[100];
}seqstack;
void push(seqstack * p,bintNode * t) /*入栈*/
{
p->data[p->top] = t;
p->top++;
}
bintNode * pop(seqstack * p)
{
if (p->top != 0) /*如果栈不为空*/
{
p->top--;
return p->data[p->top]; /*返回栈顶元素*/
}
else
{
return NULL;
}
}
/***************************************************************
*** 二叉树遍历的递归实现
****************************************************************/
/*前序遍历*/
void preorder(bintNode * t)
{
if (t)
{
printf("%c", t->info);
preorder(t->lchild);
preorder(t->rchild);
}
}
/*中序遍历*/
void inorder(bintNode * t)
{
if (t)
{
inorder(t->lchild);
printf("%c", t->info);
inorder(t->rchild);
}
}
/*后序遍历*/
void postorder(bintNode * t)
{
if (t)
{
postorder(t->lchild);
postorder(t->rchild);
printf("%c", t->info);
}
}
/***************************************************************
*** 二叉树遍历的非递归实现
****************************************************************/
/*前序遍历*/
void preorder1(bintNode * s)
{
seqstack seq;
seq.top = 0; /*初始化栈*/
while (seq.top != 0 || (s))
{
if (s)
{
printf("%c", s->info);
push(&seq, s);
s = s->lchild;
}
else
{
s=pop(&seq);
s = s->rchild;
}
}
}
/*中序遍历*/
void inorder1(bintNode * s)
{
seqstack seq;
seq.top = 0; /*初始化栈*/
while (seq.top != 0 || (s))
{
if (s)
{
push(&seq, s);
s = s->lchild;
}
else
{
s = pop(&seq);
printf("%c", s->info);
s = s->rchild;
}
}
}
/*后序遍历*/
void postorder1(bintNode * s)
{
seqstack seq;
seq.top = 0; /*初始化栈*/
while (seq.top != 0 || (s))
{
if (s)
{
seq.data[seq.top] = s; /*应为需要定义tag,不能使用push*/
seq.tag[seq.top] = 0;
seq.top++;
s = s->lchild;
}
else
{
if (seq.tag[seq.top - 1] == 1)
{
seq.top--;
s = seq.data[seq.top];
printf("%c", s->info);
s = NULL;
}
else
{
s = seq.data[seq.top - 1];
seq.tag[seq.top-1] = 1;
s = s->rchild;
}
}
}
}
/***************************************************************
*** 二叉树的创建
****************************************************************/
/*根据前序遍历创建*/
bintNode *createbintree()
{
char ch;
bintNode *t;
if ((ch = getchar()) == '#')
t = NULL;
else
{
t = (bintNode *)malloc(sizeof(bintNode));
t->info = ch;
t->lchild = createbintree();
t->rchild = createbintree();
}
return t;
}
/*根据中序遍历创建*/
bintNode *createbintree1()
{
char ch;
bintNode *t;
if ((ch = getchar()) == '#')
t = NULL;
else
{
t = (bintNode *)malloc(sizeof(bintNode));
t->lchild = createbintree1();
t->info = ch;
t->rchild = createbintree1();
}
return t;
}
int main()
{
bintNode * s;
s = createbintree(); /*通过前序遍历创建二叉树*/
printf("\n递归前序遍历\n");
preorder(s);
printf("\n非递归前序遍历\n");
preorder1(s);
printf("\n递归中序遍历\n");
inorder(s);
printf("\n非递归中序遍历\n");
inorder1(s);
printf("\n递归后序遍历\n");
postorder(s);
printf("\n非递归后序遍历\n");
postorder1(s);
system("pause");
return 0;
}