介绍:
二叉树的遍历方式分别有:
- 递归遍历
- 利用栈进行循环遍历
- 利用队列进行层次遍历
三种遍历的时间复杂度及空间复杂度均为O(N),N为树的节点数
实现:
首先,二叉树节点定义如下
typedef struct Bitree{
char data;
Bitree* lchild;
Bitree* rchild;
}Bitree;
递归遍历法(先序):
void PreorderTraverse(Bitree* T) {
if (!T) {
return;
}
else {
printf("%-3c", T->data);
PreorderTraverse(T->lchild);
PreorderTraverse(T->rchild);
}
}
该算法逻辑为:若传入参数为空指针则退出,反之则打印该节点的值,并再次对该节点的左右子树进行递归。
可通过改变第6、7、8行的顺序来实现中序遍历、后序遍历等
栈的循环遍历法:
void InorderTraverse(Bitree* T) {
Stack S = InitStack();
Bitree* P = T;
while (P || (S.buttom != S.top))
{
if (P) {
PushStack(&S, P);
P = P->lchild;
}
else {
T = PopStack(&S);
printf("%-3c", T->data);
P = T->rchild;
}
}
}
该算法逻辑为:需要利用一个栈区S和两个存放树节点的指针P和T。创建完成上述变量后进入循环,循环条件为:栈区不为空或调用的节点不是空指针。
进入循环后,首先判断节点是否为空
若不为空则将该节点压入栈区并将该节点指向左子节点,一直把一条线上的左子节点存完;
若节点为空则弹栈,并利用T来存储弹出的节点,该节点便是当前P节点的亲节点,并将其值进行打印,随后再把P指向T的右子节点,尝试存储相邻线上的左子节点
如此循环若干次,直到遍历完所有节点
对于栈的设定仅利用线性表简单的实现了基本功能,代码如下
typedef struct Stack {
int top;
int buttom;
}Stack;
Bitree* Treenode[100];
Stack InitStack() {
Stack S = { 0,0 };
return S;
}
void PushStack(Stack* S, Bitree* Tnode) {
if (S->top < 100) {
Treenode[S->top++] = Tnode;
}
else {
printf("栈区已满\n");
}
}
Bitree* PopStack(Stack* S) {
if (S->top >= S->buttom) {
return Treenode[--S->top];
}
else {
printf("栈区为空,无法弹栈\n");
}
}
队列的层次遍历法:
void Levelorder(Bitree* T) {
SqQueue Q = InitSqQueue();
EnterQue(&Q, T);
while (Q.buttom != Q.top) {
T = PopQue(&Q);
printf("%-3c", T->data);
if (T->lchild)
EnterQue(&Q, T->lchild);
if (T->rchild)
EnterQue(&Q, T->rchild);
}
}
该算法实现了对二叉树按照顺序进行节点的遍历,原理是利用了队列先入先出的特点,在循环之前先把根节点压入,进入循环后弹出压入的节点进行数值的打印并再次按顺序压入左右子节点(前提是节点为非空),然后再依次弹出,并压入各自的左右子节点
就这样,实现了按顺序压入树的各个节点并按顺序进行遍历的效果
关于队列也仅仅利用线性表实现了基本功能,代码如下
Bitree* Treequeue[100];
typedef struct SqQueue {
int top;
int buttom;
}SqQueue;
SqQueue InitSqQueue() {
SqQueue Q = { 0,0 };
return Q;
}
void EnterQue(SqQueue* Q, Bitree* Tnode) {
if (Q->top < 100) {
Treequeue[Q->top++] = Tnode;
}
else {
printf("队列已满\n");
}
}
Bitree* PopQue(SqQueue* Q) {
if (Q->buttom != Q->top) {
return Treequeue[Q->buttom++];
}
else
printf("队列为空\n");
}
效果展示