建立如下图所示的二叉树,以下所有的遍历操作均基于该二叉树
先来用递归的方式遍历二叉树
因二叉树的存储与建立均可用递归方式,且二叉树与递归息息相关,故用递归方式遍历二叉树简单明了,二叉树的遍历方式如下:
- 先序 根左右
- 中序 左根有
- 后序 左右根
- 层次遍历 只用非递归方式实现
先序遍历(递归):
void preOrder(LPTREE root) {
if (root) {
//printCurrentNode()访问根 再依次递归调用本身访问左孩子和右孩子
printCurrentNode(root); //该函数在后文有定义 用于打印节点信息
preOrder(root->LChild);
preOrder(root->RChild);
}
}
中序遍历(递归):
//中序遍历按根左右顺序 先访问左孩子 当无左孩子输出结点信息 再访问右孩子
void midOrder(LPTREE root) {
if (root) {
midOrder(root->LChild);
printCurrentNode(root);
midOrder(root->RChild);
}
}
后序遍历(递归):
void lastOrder(LPTREE root) {
if (root) {
lastOrder(root->LChild);
lastOrder(root->RChild);
printCurrentNode(root);
}
}
以上三种递归遍历较为简单,真正的难点在于非递归遍历,需要借助栈来实现,栈又会涉及到结构体,非递归遍历最考验基本功
先来定义节点信息,如下(上为图示,下为代码):
typedef struct treeNode {
char data;
struct treeNode* LChild;
struct treeNode* RChild;
} TREE, * LPTREE;
先序非递归遍历的实现如下:
void preOrderByStack(LPTREE root) {
if (root == NULL) return;
LPTREE stack[10]; //规定栈的规模
int stackTop = -1; //定义栈底指针
LPTREE pMove = root; //pMove为工作指针
while (stackTop!=-1||pMove) { //当栈有元素或工作指针不为空可进入循环
while (pMove) { //找到最下层左孩子
printf("%c\t", pMove->data);
stack[++stackTop] = pMove;
pMove = pMove->LChild;
}
if (stackTop!=-1) { //该节点子树为空 出栈
pMove = stack[stackTop--];
pMove = pMove->RChild; //指针指向当前节点的右孩子
}
}
}
中序非递归遍历实现如下:
void midOrderByStack(LPTREE root) {
if (root == NULL) return;
struct treeNode* stack[10];
int stackTop = -1;
LPTREE pMove = root;
while (stackTop!=-1||pMove) {
while (pMove) {
stack[++stackTop] = pMove;
pMove = pMove->LChild;
}
if (stackTop != -1) {
pMove = stack[stackTop--];
printf("%c\t", pMove->data);
pMove = pMove->RChild;
}
}
}
后序非递归遍历实现如下:
void lastOrderByStack(LPTREE root) {
if (root == NULL) return;
LPTREE stack[10];
int stackTop = -1;
LPTREE pMove = root;
LPTREE pLastVisit = NULL;//访问标记
while (pMove) { //先找到最下端的左孩子 一旦为空即退出循环
stack[++stackTop] = pMove;
pMove = pMove->LChild;
}
while (stackTop!=-1) {
pMove = stack[stackTop--];
if (pMove->RChild == NULL || pMove->RChild == pLastVisit) {
printf("%c\t", pMove->data); //右孩子存在或访问过 打印当前节点
pLastVisit = pMove; //标记当前节点的访问状态 为已访问
} else { //若右孩子存在且未访问过 指针指向右孩子
stack[++stackTop] = pMove;
pMove = pMove->RChild;
while (pMove) { //假若右孩子有左子树 则右孩子的左孩子入栈
stack[++stackTop] = pMove;
pMove = pMove->LChild;
}
}
}
}
层次遍历代码如下:
void levelOrder(LPTREE root){
if(!root) return;
int front=-1,rear=-1; //front指向队首 rear指队尾
LPTREE pMove=root;
LPTREE Q[10]; //创建辅助队列 层次遍历需要借助队列实现
Q[++rear]=root;
while(front!=rear){
pMove=Q[++front]; //出队
printf("%c\t",pMove->data);
if(pMove->LChild){
Q[++rear]=pMove->LChild;
}
if(pMove->RChild) {
Q[++rear]=pMove->RChild;
}
}
}
求结点的度算法如下:
算法思想:采用任意一种遍历 以下选用先序遍历 设置一个计数变量cnt(conut的缩写) 记录当前节点的子结点数,若判断当前节点的左右孩子是否存在,若存在则累加
void countNum(LPTREE root){
if(!root) return;
int cnt=0;
if(root->LChild) ++cnt;
if(root->RChild) ++cnt;
printf("节点%c的度为%d\n",root->data,cnt);
if(root->LChild)
countNum(root->LChild);
if(root->RChild)
countNum(root->RChild);
}
完整代码如下:
#include<stdio.h>
#include<cstring>
#include<cstdlib>
typedef struct treeNode {
char data;
struct treeNode* LChild;
struct treeNode* RChild;
} TREE, * LPTREE;
LPTREE createNode(char data) { //插入字符数据
LPTREE newNode = (LPTREE)malloc(sizeof(TREE)); //创建节点 分配节点空间
newNode->data = data;
newNode->LChild = NULL;
newNode->RChild = NULL;
return newNode;
}
void insertNode(LPTREE parentNode, LPTREE LChild, LPTREE RChild) {
parentNode->LChild = LChild;
parentNode->RChild = RChild;
}
void printCurrentNode(LPTREE currentData) {
printf("%c\t", currentData->data);
}
//先序递归
void preOrder(LPTREE root) {
if (root) {
printCurrentNode(root);
preOrder(root->LChild);
preOrder(root->RChild);
}
}
//中序递归
void midOrder(LPTREE root) {
if (root) {
midOrder(root->LChild);
printCurrentNode(root);
midOrder(root->RChild);
}
}
//后序递归
void lastOrder(LPTREE root) {
if (root) {
lastOrder(root->LChild);
lastOrder(root->RChild);
printCurrentNode(root);
}
}
//先序朴素
void preOrderByStack(LPTREE root) {
if (root == NULL) return;
LPTREE stack[10];
int stackTop = -1;
LPTREE pMove = root;
while (stackTop!=-1||pMove) {
while (pMove) {
printf("%c\t", pMove->data);
stack[++stackTop] = pMove;
pMove = pMove->LChild;
}
if (stackTop!=-1) {
pMove = stack[stackTop--];
pMove = pMove->RChild; //指针指向当前节点的右孩子
}
}
}
//中序朴素
void midOrderByStack(LPTREE root) {
if (root == NULL) return;
struct treeNode* stack[10];
int stackTop = -1;
LPTREE pMove = root;
while (stackTop!=-1||pMove) {
while (pMove) {
stack[++stackTop] = pMove;
pMove = pMove->LChild;
}
if (stackTop != -1) {
pMove = stack[stackTop--];
printf("%c\t", pMove->data);
pMove = pMove->RChild;
}
}
}
//后序朴素
void lastOrderByStack(LPTREE root) {
if (root == NULL) return;
LPTREE stack[10];
int stackTop = -1;
LPTREE pMove = root;
LPTREE pLastVisit = NULL;//访问标记
while (pMove) { //先找到最下端的左孩子 一旦为空即退出循环
stack[++stackTop] = pMove;
pMove = pMove->LChild;
}
while (stackTop!=-1) {
pMove = stack[stackTop--];
if (pMove->RChild == NULL || pMove->RChild == pLastVisit) {
printf("%c\t", pMove->data); //右孩子存在或访问过 打印当前节点
pLastVisit = pMove; //标记当前节点的访问状态 为已访问
} else { //若右孩子存在且未访问过 指针指向右孩子
stack[++stackTop] = pMove;
pMove = pMove->RChild;
while (pMove) { //假若右孩子有左子树 则右孩子的左孩子入栈
stack[++stackTop] = pMove;
pMove = pMove->LChild;
}
}
}
}
//层次遍历
void levelOrder(LPTREE root){
if(!root) return;
int front=-1,rear=-1;
LPTREE pMove=root;
LPTREE Q[10]; //创建辅助队列 层次遍历需要借助队列实现
Q[++rear]=root;
while(front!=rear){
pMove=Q[++front]; //出队
printf("%c\t",pMove->data);
if(pMove->LChild){
Q[++rear]=pMove->LChild;
}
if(pMove->RChild) {
Q[++rear]=pMove->RChild;
}
}
}
//计算结点的度 思想:遍历二叉树 判断结点是否有子结点 有则累加 每次输出
void countNum(LPTREE root){
if(!root) return;
int cnt=0;
if(root->LChild) ++cnt;
if(root->RChild) ++cnt;
printf("节点%c的度为%d\n",root->data,cnt);
if(root->LChild)
countNum(root->LChild);
if(root->RChild)
countNum(root->RChild);
}
//主函数
int main() {
LPTREE A = createNode('A');
LPTREE B = createNode('B');
LPTREE C = createNode('C');
LPTREE D = createNode('D');
LPTREE E = createNode('E');
LPTREE F = createNode('F');
LPTREE G = createNode('G');
insertNode(A, B, C);
insertNode(B, D, NULL);
insertNode(D, NULL, G);
insertNode(C, E, F);
printf("先序遍历(递归):\n");
preOrder(A); //从A开始先序遍历
printf("\n先序遍历(非递归):\n");
preOrderByStack(A);
printf("\n中序遍历(递归):\n");
midOrder(A);
printf("\n中序遍历(非递归):\n");
midOrderByStack(A);
printf("\n后序遍历(递归):\n");
lastOrder(A);
printf("\n后序遍历(非递归):\n");
lastOrderByStack(A);
printf("\n层次遍历:\n");
levelOrder(A);
printf("\n");
countNum(A);
}
最终效果图如下: