前言
四种基本的遍历思想
- 先(前)序遍历:根结点 —> 左子树 —> 右子树
- 中序遍历:左子树—>根结点—> 右子树
- 后序遍历:左子树 —> 右子树—> 根结点
- 层次遍历:仅仅需按层次遍历就可以
如图所示二叉树
先序遍历结果为: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;
一、先序遍历
递归遍历
算法C语言实现:
void PreOrderTraverse(BiTree T) {if (T != NULL) {printf("%d ", T->data);PreOrderTraverse(T->left);//递归遍历左子树PreOrderTraverse(T->right);//递归遍历右子树}}
迭代遍历
算法C语言实现:
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;}}
二、中序遍历
递归遍历
算法C语言实现:
void InOrderTraverse(BiTree T) {if (T != NULL) {PreOrderTraverse(T->left);//递归遍历左子树printf("%d ", T->data);PreOrderTraverse(T->right);//递归遍历右子树}}
迭代遍历
算法C语言实现:
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指向结点的右子树}}
三、后序遍历
递归遍历
算法C语言实现:
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 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");}}