图解二叉树遍历方法-前序遍历、中序遍历、后序遍历

一、几个概念

二叉树(binary tree):是 n(n >= 0)个结点(每个结点最多只有2棵子树)的有限集合,该集合可为空集(称为空二叉树),或由一个根节点和两颗互不相交的,称为根节点的左子树和右子树的二叉树组成。

如下图中

图1是一棵二叉树。

图2是非二叉树,因为 A 结点有3棵子树,其次 E 结点和 F 结点相交了

二叉链表:是二叉树的一种链式存储结构,其中每个结点包含三个字段:一个数据字段(data)和两个指针字段(*lchild、*rchild),分别指向该结点的左孩子和右孩子。如果某个结点没有左孩子或右孩子,那么对应的指针字段为NULL。

用 C 语言可以表述为:

typedef struct binary_node {char data;struct binary_node *lchild, *rchild;
}binary_tree;

二、前序遍历二叉树(Pre-order Traversal

        前序遍历二叉树(Pre-order Traversal)的规则为:从根结点出发,先访问该结点,然后前序遍历该结点的左子树,再然后前序遍历该结点的右子树

所以,前序遍历图1这棵二叉树的步骤如图4所示

所以,图1这颗二叉树的前序遍历顺序为:ABDECF

1、算法思路

(1)从根结点 A 出发,A 结点入栈,先访问 A 结点,然后前序遍历 A 结点的左子树

(2)A 结点的左子树的根结点为 B,B 结点入栈,先访问 B 结点,然后前序遍历 B 结点的左子树

(3)B 结点的左子树的根结点为 D,D 结点入栈,先访问 D 结点,然后前序遍历 D 结点的左子树

(4)D 结点的左子树为空,则返回

(5)逻辑返回到 D 结点,然后前序遍历 D 结点的右子树

(6)D 结点的右子树为空,则返回

(7)逻辑返回到 D 结点,此时访问 D 结点,前序遍历 D 结点的左右子树的逻辑都已完成,则返回,D 结点出栈。

(8)逻辑返回到 B 结点,然后前序遍历 B 结点的右子树

(9)B 结点的右子树的根结点为 E,E 结点入栈,先访问 E 结点,然后前序遍历 E 结点的左子树

(10)E 结点的左子树为空,则返回

(11)逻辑返回到 E 结点,然后前序遍历 E 结点的右子树

(12)E 结点的右子树为空,则返回

(13)逻辑返回到 E 结点,此时访问 E 结点,前序遍历 E 结点的左右子树的逻辑都已完成,则返回,E 结点出栈。

(14)逻辑返回到 B 结点,此时访问 B 结点,前序遍历 B 结点的左右子树的逻辑都已完成,则函数返回,B 结点出栈。

(15)逻辑返回到 A 结点,然后前序遍历 A 结点的右子树

(16)A 结点的右子树的根结点为 C,C 结点入栈,先访问 C 结点,然后前序遍历 C 结点的左子树

(17)C 结点的左子树的根结点为 F,F 结点入栈,先访问 F 结点,然后前序遍历 F 结点的左子树

(18)F 结点的左子树为空,则返回

(19)逻辑返回到 F 结点,然后前序遍历 F 结点的右子树

(20)F 结点的右子树为空,则返回

(21)逻辑返回到 F 结点,此时访问 F 结点,前序遍历 F 结点的左右子树的逻辑都已完成,则返回,F 结点出栈。

(22)逻辑返回到 C 结点,然后前序遍历 C 结点的右子树

(23)C 结点的右子树为空,则返回

(24)逻辑返回到 C 结点,此时访问 C 结点,前序遍历 C 结点的左右子树的逻辑都已完成,则返回,C 结点出栈。

(25)逻辑返回到 A 结点,此时访问 A 结点,前序遍历 A 结点的左右子树的逻辑都已完成,则返回,A 结点出栈。

(26)前序遍历二叉树已完成, 所以,前序遍历顺序为:ABDECF

2、实现代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>char *g_str = "ABD##E##CF###"; // 扩展二叉树前序序列
int g_index = 0;
typedef struct binary_node {char data;struct binary_node *lchild, *rchild;
}binary_tree;void create_binary_tree(binary_tree **T) {if (strlen(g_str) == 0)return;if (g_str[g_index] == '#') {*T = NULL;g_index++;} else {*T = malloc(sizeof(**T));(*T)->data = g_str[g_index];g_index++;create_binary_tree(&(*T)->lchild);create_binary_tree(&(*T)->rchild);}
}void visit_node(binary_tree *T) {printf("%c\n", T->data);
}void pre_order_tree(binary_tree *T) {if (!T)return;visit_node(T);pre_order_tree(T->lchild);pre_order_tree(T->rchild);
}/*销毁用后序遍历*/
void destroy_binary_tree(binary_tree *T) {if (!T)return;destroy_binary_tree(T->lchild);destroy_binary_tree(T->rchild);printf("%c\n", T->data);free(T);
}int main(int argc, char *argv[]) {binary_tree *T;create_binary_tree(&T);printf("------前序遍历-------\n");pre_order_tree(T);printf("------销毁二叉树------\n");destroy_binary_tree(T);return 0;
}

三、中序遍历二叉树(In-order Traversal

        中序遍历二叉树(In-order Traversal)的规则为:从根结点出发,先中序遍历该结点的左子树,然后访问该结点,再然后中序遍历该结点的右子树

所以,中序遍历图1这棵二叉树的步骤如下图所示

所以,图1这颗二叉树中序遍历顺序为:DBEAFC

1、算法思路

(1)从根结点 A 出发,A 结点入栈,先中序遍历 A 结点的左子树

(2)A 结点的左子树的根结点为 B,B 结点入栈,先中序遍历 B 结点的左子树

(3)B 结点的左子树的根结点为 D,D 结点入栈,先中序遍历 D 结点的左子树

(4)D 结点的左子树为空,则返回

(5)逻辑返回到 D 结点,然后访问 D 结点,再然后中序遍历 D 结点的右子树

(6)D 结点的右子树为空,则返回

(7)逻辑返回到 D 结点,此时中序遍历 D 结点的左子树,访问 D 结点,中序遍历 D 结点的右子树的逻辑都已完成,则返回,D 结点出栈。

(8)逻辑返回到 B 结点,然后访问 B 结点,再然后中序遍历 B 结点的右子树

(9)B 结点的右子树的根结点为 E,E 结点入栈,先中序遍历 E 结点的左子树

(10)E 结点的左子树为空,则返回

(11)逻辑返回到 E 结点,然后访问 E 结点,再然后中序遍历 E 结点的右子树

(12)E 结点的右子树为空,则返回

(13)逻辑返回到 E 结点,此时中序遍历 E 结点的左子树,访问 E 结点,中序遍历 E 结点的右子树的逻辑都已完成,则返回,E 结点出栈。

(14)逻辑返回到 B 结点,此时中序遍历 B 结点的左子树,访问 B 结点,中序遍历 B 结点的右子树的逻辑都已完成,则返回,B 结点出栈。

(15)逻辑返回到 A 结点,然后访问 A 结点,再然后中序遍历 A 结点的右子树

(16)A 结点的右子树的根结点为 C,C 结点入栈,先中序遍历 C 结点的左子树

(17)C 结点的左子树的根结点为 F,F 结点入栈,先中序遍历 F 结点的左子树

(18)F 结点的左子树为空,则返回

(19)逻辑返回到 F 结点,然后访问 F 结点,再然后中序遍历 F 结点的右子树

(20)F 结点的右子树为空,则返回

(21)逻辑返回到 F 结点,此时中序遍历 F 结点的左子树,访问 F 结点,中序遍历 F 结点的右子树的逻辑都已完成,则返回,F 结点出栈。

(22)逻辑返回到 C 结点,然后访问 C 结点,再然后中序遍历 C 结点的右子树

(23)C 结点的右子树为空,则返回

(24)逻辑返回到 C 结点,此时中序遍历 C 结点的左子树,访问 C 结点,中序遍历 C 结点的右子树的逻辑都已完成,则返回,C 结点出栈。

(25)逻辑返回到 A 结点,此时中序遍历 A 结点的左子树,访问 A 结点,中序遍历 A 结点的右子树的逻辑都已完成,则返回,A 结点出栈。

(26)中序遍历二叉树已完成, 所以,中序遍历顺序为:DBEAFC

2、实现代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>char *g_str = "ABD##E##CF###"; // 扩展二叉树前序序列
int g_index = 0;
typedef struct binary_node {char data;struct binary_node *lchild, *rchild;
}binary_tree;void create_binary_tree(binary_tree **T) {if (strlen(g_str) == 0)return;if (g_str[g_index] == '#') {*T = NULL;g_index++;} else {*T = malloc(sizeof(**T));(*T)->data = g_str[g_index];g_index++;create_binary_tree(&(*T)->lchild);create_binary_tree(&(*T)->rchild);}
}void visit_node(binary_tree *T) {printf("%c\n", T->data);
}void pre_order_tree(binary_tree *T) {if (!T)return;visit_node(T);pre_order_tree(T->lchild);pre_order_tree(T->rchild);
}void in_order_tree(binary_tree *T) {if (!T)return;in_order_tree(T->lchild);visit_node(T);in_order_tree(T->rchild);
}/*销毁用后序遍历*/
void destroy_binary_tree(binary_tree *T) {if (!T)return;destroy_binary_tree(T->lchild);destroy_binary_tree(T->rchild);printf("%c\n", T->data);free(T);
}int main(int argc, char *argv[]) {binary_tree *T;create_binary_tree(&T);printf("------中序遍历-------\n");in_order_tree(T);printf("------销毁二叉树------\n");destroy_binary_tree(T);return 0;
}

四、后序遍历二叉树(Post-order Traversal

        后序遍历二叉树(Post-order Traversal)的规则为:从根结点出发,先后序遍历该结点的左子树,然后后序遍历该结点的右子树,再然后访问该结点

所以,后序遍历图1这棵二叉树的步骤如下图所示

所以,图1这颗二叉树后序遍历顺序为:DEBFCA

1、算法思路

(1)从根结点 A 出发,A 结点入栈,先后序遍历 A 结点的左子树

(2)A 结点的左子树的根结点为 B,B 结点入栈,先后序遍历 B 结点的左子树

(3)B 结点的左子树的根结点为 D,D 结点入栈,先后序遍历 D 结点的左子树

(4)D 结点的左子树为空,则返回

(5)逻辑返回到 D 结点,然后后序遍历 D 结点的右子树

(6)D 结点的右子树为空,则返回

(7)逻辑返回到 D 结点,然后访问 D 结点,此时后序遍历 D 结点的左子树,后序遍历 D 结点的右子树,访问 D 结点的逻辑都已完成,则返回,D 结点出栈。

(8)逻辑返回到 B 结点,然后后序遍历 B 结点的右子树

(9)B 结点的右子树的根结点为 E,E 结点入栈,先后序遍历 E 结点的左子树

(10)E 结点的左子树为空,则返回

(11)逻辑返回到 E 结点,然后后序遍历 E 结点的右子树

(12)E 结点的右子树为空,则返回

(13)逻辑返回到 E 结点,然后访问 E 结点,此时后序遍历 E 结点的左子树,后序遍历 E 结点的右子树,访问 E 结点的逻辑都已完成,则返回,E 结点出栈。

(14)逻辑返回到 B 结点,然后访问 B 结点,此时后序遍历 B 结点的左子树,后序遍历 B 结点的右子树,访问 B 结点的逻辑都已完成,则返回,B 结点出栈。

(15)逻辑返回到 A 结点,然后后序遍历 A 结点的右子树

(16)A 结点的右子树的根结点为 C,C 结点入栈,先后序遍历 C 结点的左子树

(17)C 结点的左子树的根结点为 F,F 结点入栈,先后序遍历 F 结点的左子树

(18)F 结点的左子树为空,则返回

(19)逻辑返回到 F 结点,然后后序遍历 F 结点的右子树

(20)F 结点的右子树为空,则返回

(21)逻辑返回到 F 结点,然后访问 F 结点,此时后序遍历 F 结点的左子树,后序遍历 F 结点的右子树,访问 F 结点的逻辑都已完成,则返回,F 结点出栈。

(22)逻辑返回到 C 结点,然后后序遍历 C 结点的右子树

(23)C 结点的右子树为空,则返回

(24)逻辑返回到 C 结点,然后访问 C 结点,此时后序遍历 C 结点的左子树,后序遍历 C 结点的右子树,访问 C 结点的逻辑都已完成,则返回,C 结点出栈。

(25)逻辑返回到 A 结点,然后访问 A 结点,此时后序遍历 A 结点的左子树,后序遍历 A 结点的右子树,访问 A 结点的逻辑都已完成,则返回,A 结点出栈。

(26)后序遍历二叉树已完成, 所以,后序遍历顺序为:DEBFCA

2、实现代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>char *g_str = "ABD##E##CF###"; // 扩展二叉树前序序列
int g_index = 0;
typedef struct binary_node {char data;struct binary_node *lchild, *rchild;
}binary_tree;void create_binary_tree(binary_tree **T) {if (strlen(g_str) == 0)return;if (g_str[g_index] == '#') {*T = NULL;g_index++;} else {*T = malloc(sizeof(**T));(*T)->data = g_str[g_index];g_index++;create_binary_tree(&(*T)->lchild);create_binary_tree(&(*T)->rchild);}
}void visit_node(binary_tree *T) {printf("%c\n", T->data);
}void pre_order_tree(binary_tree *T) {if (!T)return;visit_node(T);pre_order_tree(T->lchild);pre_order_tree(T->rchild);
}void in_order_tree(binary_tree *T) {if (!T)return;in_order_tree(T->lchild);visit_node(T);in_order_tree(T->rchild);
}void post_order_tree(binary_tree *T) {if (!T)return;post_order_tree(T->lchild);post_order_tree(T->rchild);visit_node(T);
}/*销毁用后序遍历*/
void destroy_binary_tree(binary_tree *T) {if (!T)return;destroy_binary_tree(T->lchild);destroy_binary_tree(T->rchild);printf("%c\n", T->data);free(T);
}int main(int argc, char *argv[]) {binary_tree *T;create_binary_tree(&T);printf("------后序遍历-------\n");post_order_tree(T);printf("------销毁二叉树------\n");destroy_binary_tree(T);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/617340.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Redis实现用户最近浏览记录

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Redis是一个key-va…

使用VBA巧妙获取图表数据源区域

在日常工作中&#xff0c;使用VBA操作Excel图表是经验遇到的工作创建。 示例图表如下&#xff1a; 使用如下代码可以更新图表的数据源区域&#xff0c;增加一个数据系列。 Sub UpdateChart()ActiveSheet.ChartObjects(1).ActivateActiveChart.SetSourceData Source:Range(&qu…

牛客研究生复试刷题(1)

KY30进制转换 1.最开始没有考虑到大数问题,可以说是没考虑完全,输入类型使用的是int64_t,只ac了一半测试用例。所以在数很大找不到合适的数据类型存储时,要考虑使用string来存放。 2.使用string存放数字的时候就要考虑:字符和数字之间的转换。字符转换成数字:str[i]-0,…

从 0 搭建公司Jenkins服务 Centos7

从 0 搭建公司Jenkins服务 Centos7 安装 (运维人员) 安装环境 配置DNS安装JDK17安装Jenkins安装Docker安装GIT安装Ansible启动Jenkins安装插件配置凭据配置共享库配置 (开发经理)使用 (开发、测试人员) 安装 (运维人员) 安装环境 配置DNS 新安装系统的服务器无法解析域名&a…

CMake 学习笔记2

其他很好的总结 CMake教程系列-01-最小配置示例 - 知乎 CMake 保姆级教程&#xff08;上&#xff09; | 爱编程的大丙 10-补充(完结)_哔哩哔哩_bilibili 1、基本关键字 SET命令的补充 &#xff08;1&#xff09;SET命令设置执行标准 #增加-stdc11 set(CMAKE_CXX_STANDARD…

二、Flask会话技术和模板语言

Cookie Session # views.py: 路由 + 视图函数 import datetimefrom flask import Blueprint, render_template, request, redirect, session from .models import *# 蓝图 blue = Blueprint(user, __name__)# 首页 可以写两个路由,都是访问同一个函数 @blue.route(/) @blue.ro…

【算法基础2】前缀和与差分

目录 前缀和与差分1.综述2.前缀和&#xff08;1&#xff09;一维前缀和&#xff08;2&#xff09;二维前缀和&#xff08;子矩阵的和&#xff09; 3.差分&#xff08;1&#xff09;一维差分&#xff08;2&#xff09;二维差分&#xff08;差分矩阵&#xff09; 前缀和与差分 1…

简介:基于Web的产品3D

基于 Web 的产品 3D 通过可视化界面获得各种选项来个性化他们的产品&#xff0c;例如颜色、材料、尺寸、文字、徽标、零件等。 在过去几年中&#xff0c;随着 3D 建模和渲染软件的出现&#xff0c;3D 渲染现在更常用于营销和促销目的。设计师、制造商和营销人员使用 3D 产品渲…

64B/66B编码

一、前言 8B/10B编码主要作用的优化直流平衡&#xff0c;从8bit中插2个bit进去&#xff0c;这样的话最终效果能够使长0或者长1的位数不超过5位&#xff0c;达到很好的效果。但是由于8B/10B编码的带宽利用率非常低&#xff0c;10G的带宽只有8G在传输有效数据&#xff0c;2G的带…

Java | Leetcode Java题解之第27题移除元素

题目&#xff1a; 题解&#xff1a; class Solution {public int removeElement(int[] nums, int val) {int left 0;int right nums.length;while (left < right) {if (nums[left] val) {nums[left] nums[right - 1];right--;} else {left;}}return left;} }

GAN:对抗生成网络【通俗易懂】

一、概述 对抗生成网络&#xff08;GAN&#xff09;是一种深度学习模型&#xff0c;由两个神经网络组成&#xff1a;生成器G和判别器D。这两个网络被训练来协同工作&#xff0c;以生成接近真实数据的新样本。 生成器的任务是接收一个随机噪声向量&#xff0c;并将其转换为与真…

【Linux】基础IO----理解缓冲区

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;理解缓冲区 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不会坚持。早安! > 专栏选自&#xff1a;Linux初阶 > 望…