如图所示二叉树
先序遍历结果为:1 2 4 5 3 6
中序遍历结果为:4 2 5 1 6 3
后序遍历结果为:4 5 2 6 3 1
层序遍历结果为:1 2 3 4 5 6
递归的实现就是每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
而迭代法遍历的原理就是模拟递归。
typedef struct {
int data;//数据域
struct BiTNode* left, * right;//左子树右子树
}BiTNode, * BiTree;
void PreOrderTraverse(BiTree T) {
if (T != NULL) {
printf("%d ", T->data);
PreOrderTraverse(T->left);//递归遍历左子树
PreOrderTraverse(T->right);//递归遍历右子树
}
}
void PreOrderTraverse(BiTree T) {
if (T == NULL) return;
BiTree Stack[100];
int top = 0;
Stack[top++] = T;//先把根结点入栈
BiTree p = NULL;
while (top > 0) {
p = Stack[--top];//出栈
printf("%d ", p->data);//打印结点值
//将出栈结点非空子树入栈,一定是右子树先入栈,再左子树入栈
//这样左子树就会先出栈
if (p->right != NULL) Stack[top++] = p->right;
if (p->left != NULL) Stack[top++] = p->left;
}
}
void InOrderTraverse(BiTree T) {
if (T != NULL) {
PreOrderTraverse(T->left);//递归遍历左子树
printf("%d ", T->data);
PreOrderTraverse(T->right);//递归遍历右子树
}
}
void InOrderTraverse(BiTree T) {
BiTree Stack[100];//定义个栈
int top = 0;//栈顶指针
BiTree root = T;//辅助指针
while (top > 0 || root != NULL) {
//搜索当前p指向结点的非空左子树并入栈
if (root != NULL) {
Stack[top++] = root;
root = root->left;
}
//打印根节点(也就是中间结点)
else {
root = Stack[--top];
printf("%d ", root->data);//弹出栈顶结点并打印
root = root->right;//搜索p指向结点的右子树
}
}
}
还有种写法,原理相同循环语句有点不同而已。
void InOrderTraverse(BiTree T) {
BiTree Stack[100];//定义个栈
int top = 0;//栈顶指针
BiTree root = T;//辅助指针
while (top > 0 || root != NULL) {
//搜索当前p指向结点的非空左子树并入栈
while(root != NULL) {
Stack[top++] = root;
root = root->left;
}
//打印根节点(也就是中间结点)
root = Stack[--top];
printf("%d ", root->data);//弹出栈顶结点并打印
root = root->right;//搜索p指向结点的右子树
}
}
void PostOrderTraverse(BiTree T) {
if (T != NULL) {
PreOrderTraverse(T->left);//递归遍历左子树
PreOrderTraverse(T->right);//递归遍历右子树
printf("%d ", T->data);
}
}
和先序遍历类似,可以用一个数组储存遍历结果,最后反转就是左右中了。
void reverse(int* a, int asize) {
int i, j, t;
i = 0;
j = asize - 1;
while (i < j) {
t = a[i];
a[i] = a[j];
a[j] = t;
i++;
j--;
}
}
//后序遍历
void PostOrderTraverse(BiTree T) {
if (T == NULL) return;
BiTree Stack[100];
int top = 0;
Stack[top++] = T;
BiTree p = NULL;
int result[100];//用来存放遍历结果
int resultsize = 0;//记录长度
while (top > 0) {
p = Stack[--top];//出栈
result[resultsize++] = p->data;
//将出栈结点非空子树入栈,左子树先入栈,再右子树入栈
//这样将结果反转就是左右中了
if (p->left != NULL) Stack[top++] = p->left;
if (p->right != NULL) Stack[top++] = p->right;
}
reverse(result, resultsize);//将结果反转
//输出
for (int i = 0; i < resultsize; i++) {
printf("%d ", result[i]);
}
}
其实这种方法遍历本质上还是前序遍历,只是把结果反转了,并不能称得上真正的后序遍历。
以下这种后序遍历方法与前中序遍历不同的是借助了一个指针用来标记。
void PostOrderTraverse(BiTree T) {
if (T == NULL) return;
BiTree Stack[100];
int top = 0;
BiTree prev = NULL;//用来标记,被标记的结点及其子树都是已经遍历完了的。
BiTree root = T;//根节点指针
while (top > 0 || root != NULL) {
//把当前根节点入栈以及非空左子树入栈
while (root != NULL) {
Stack[top++] = root;
root = root->left;
}
root = Stack[--top];//弹出左子树被完全遍历的结点
//root->right==NULL当前根节点右子树为空,root->right==prev当前根节点右子树被完全遍历过
if (root->right == NULL || root->right == prev) {
printf("%d ", root->data);//左右子树都被遍历完,打印当前根节点值。
prev = root;//标记当前根节点,用来判断后续结点的右子树是否被完全遍历。
root = NULL;//根节点指针置空,为了回溯到父节点。
}
//将当前根节点入栈,遍历右子树
else {
Stack[top++] = root;
root = root->right;
}
}
}
需要用队列作为辅助结构,我们先将根节点放到队列中,然后不断遍历队列。
首先拿出根节点,如果左子树/右子树不为空,就将他们放入队列中。第一遍处理完后,根节点已经从队列中拿走了,而根节点的两个孩子已放入队列中了,现在队列中就有两个节点 2 和 5。
第二次处理,会将 2
和 5
这两个节点从队列中拿走,然后再将 2 和 5 的子节点放入队列中,现在队列中就有三个节点 3
,4
,6
。
我们把每层遍历到的节点都放入到一个结果集中,最后返回这个结果集就可以了。
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
//队列结点
typedef struct QNode {
BiTree data;
struct QNode* next;
}QNode;
//队列
typedef struct {
QNode* front;
QNode* rear;
int n;//用来记录队列长度
}BiTQueue;
//队列入队操作
void Push(BiTQueue* Q, BiTree T) {
QNode* node = malloc(sizeof(QNode));
node->data = T;
node->next = NULL;
Q->rear->next = node;
Q->rear = node;
Q->n++;
}
//队列出队操作
BiTree Pop(BiTQueue* Q) {
BiTree T = Q->front->next->data;
if (Q->front->next == Q->rear) Q->rear = Q->front;
Q->front->next = Q->front->next->next;
Q->n--;
return T;
}
//层序遍历函数
void LeveOrderTraverse(BiTree T) {
if (T == NULL) return;
BiTQueue* Q = InitQueue();//初始化队列
BiTree p = NULL;
Push(Q, T);//将根结点入队
while (Q->n > 0) {
//只取n个队列元素
for (int i = Q->n; i > 0; i--) {
p = Pop(Q);//出队
printf("%d ", p->data);//打印结点值
if (p->left != NULL) Push(Q, p->left);//左子树入队
if (p->right != NULL) Push(Q, p->right);//右子树入队
}
printf("\n");
}
}
看别人的代码很难懂,不妨跟着敲一遍,然后把数据代进去走一遍流程,就很容易懂啦!