二叉树的前序、中序、后序遍历及互相求法
主要内容转自http://blog.csdn.net/fansongy,例子转自https://www.zybang.com/question/eacbb6b83a26339e9af3287a7266a76e.html,所有权力归原作者所有。
一、基本概念
每个结点最多有两棵子树,左子树和右子树,次序不可以颠倒。
性质:
1、非空二叉树的第n层上至多有2^(n-1)个元素。
2、深度为h的二叉树至多有2^h-1个结点。
满二叉树:所有终端都在同一层次,且非终端结点的度数为2。
在满二叉树中若其深度为h,则其所包含的结点数必为2^h-1。
完全二叉树:除了最大的层次即成为一颗满二叉树且层次最大那层所有的结点均向左靠齐,即集中在左面的位置上,不能有空位置。
对于完全二叉树,设一个结点为i则其父节点为i/2,2i为左子节点,2i+1为右子节点。
二、存储结构
顺序存储:
将数据结构存在一块固定的数组中。
[cpp] view plaincopy print?
- #define LENGTH 100
- typedef char datatype;
- typedef struct node{
- datatype data;
- int lchild,rchild;
- int parent;
- }Node;
- Node tree[LENGTH];
- int length;
- int root;
虽然在遍历速度上有一定的优势,但因所占空间比较大,是非主流二叉树。二叉树通常以链式存储。
链式存储:
[cpp] view plaincopy print?
- typedef char datatype;
- typedef struct BinNode{
- datatype data;
- struct BinNode* lchild;
- struct BinNode* rchild;
- }BinNode;
- typedef BinNode* bintree; //bintree本身是个指向结点的指针
三、二叉树的遍历
遍历即将树的所有结点访问且仅访问一次。按照根节点位置的不同分为前序遍历,中序遍历,后序遍历。
前序遍历:根节点->左子树->右子树
中序遍历:左子树->根节点->右子树
后序遍历:左子树->右子树->根节点
例如:求下面树的三种遍历
前序遍历:abdefgc
中序遍历:debgfac
后序遍历:edgfbca
一个例子:
假设某二叉树的先序遍历序列是abdgcefh,中序遍历序列是dgbaechf,画出二叉树,并给出其后序遍历序列.
以下面的例题为例进行讲
已知一棵二叉树的先序遍历序列和中序遍历序列分别是abdgcefh、dgbaechf,求二叉树及后序遍历序列.
分析:先序遍历序列的第一个字符为根结点.对于中序遍历,根结点在中序遍历序列的中间,左边部分是根结点的左子树的中序遍历序列,右边部分是根结点的右子树的中序遍历序列.
先序:abdgcefh --> a bdg cefh
中序:dgbaechf --> dgb a echf
得出结论:a是树根,a有左子树和右子树,左子树有bdg结点,右子树有cefh结点.
先序:bdg --> b dg
中序:dgb --> dg b
得出结论:b是左子树的根结点,b无右子树,有左子树.
先序:dg --> d g
中序:dg --> d g
得出结论:d是b的左子树的根结点,d无左子树,有右子树.
先序:cefh --> c e fh
中序:echf --> e c hf
得出结论:c是右子树的根结点,c有左子树(只有e结点),有右子树(有fh结点).
先序:fh --> f h
中序:hf --> h f
得出结论:f是c的左子树的根结点,f有左子树(只有h结点),无右子树.
还原二叉树为:
a
b c
d e f
g h
后序遍历序列:gdbehfca
四、遍历的实现
递归实现(以前序遍历为例,其他的只是输出的位置稍有不同)
[cpp] view plaincopy print?
- void preorder(bintree t){
- if(t){
- printf("%c ",t->data);
- preorder(t->lchild);
- preorder(t->rchild);
- }
- }
非递归的实现
因为当遍历过根节点之后还要回来,所以必须将其存起来。考虑到后进先出的特点,选用栈存储。数量确定,以顺序栈存储。
[cpp] view plaincopy print?
- #define SIZE 100
- typedef struct seqstack{
- bintree data[SIZE];
- int tag[SIZE]; //为后续遍历准备的
- int top; //top为数组的下标
- }seqstack;
- void push(seqstack *s,bintree t){
- if(s->top == SIZE){
- printf("the stack is full\n");
- }else{
- s->top++;
- s->data[s->top]=t;
- }
- }
- bintree pop(seqstack *s){
- if(s->top == -1){
- return NULL;
- }else{
- s->top--;
- return s->data[s->top+1];
- }
- }
1、前序遍历
[cpp] view plaincopy print?
- void preorder_dev(bintree t){
- seqstack s;
- s.top = -1; //因为top在这里表示了数组中的位置,所以空为-1
- if(!t){
- printf("the tree is empty\n");
- }else{
- while(t || s.stop != -1){
- while(t){ //只要结点不为空就应该入栈保存,与其左右结点无关
- printf("%c ",t->data);
- push(&s,t);
- t= t->lchild;
- }
- t=pop(&s);
- t=t->rchild;
- }
- }
- }
2、中序遍历
[cpp] view plaincopy print?
- void midorder(bintree t){
- seqstack s;
- s.top = -1;
- if(!t){
- printf("the tree is empty!\n");
- }else{
- while(t ||s.top != -1){
- while(t){
- push(&s,t);
- t= t->lchild;
- }
- t=pop(&s);
- printf("%c ",t->data);
- t=t->rchild;
- }
- }
- }
3、后序遍历
因为后序遍历最后还要要访问根结点一次,所以要访问根结点两次。采取夹标志位的方法解决这个问题。
这段代码非常纠结,对自己有信心的朋友可以尝试独立写一下。反正我是写了很长时间。逻辑不难,我画了一张逻辑图:
代码:
[cpp] view plaincopy print?
- void postorder_dev(bintree t){
- seqstack s;
- s.top = -1;
- if(!t){
- printf("the tree is empty!\n");
- }else{
- while(t || s.top != -1){ //栈空了的同时t也为空。
- while(t){
- push(&s,t);
- s.tag[s.top] = 0; //设置访问标记,0为第一次访问,1为第二次访问
- t= t->lchild;
- }
- if(s.tag[s.top] == 0){ //第一次访问时,转向同层右结点
- t= s.data[s.top]; //左走到底时t是为空的,必须有这步!
- s.tag[s.top]=1;
- t=t->rchild;
- }else {
- while (s.tag[s.top] == 1){ //找到栈中下一个第一次访问的结点,退出循环时并没有pop所以为其左子结点
- t = pop(&s);
- printf("%c ",t->data);
- }
- t = NULL; //必须将t置空。跳过向左走,直接向右走
- }
- }
- }
- }
4、层次遍历:即每一层从左向右输出
元素需要储存有先进先出的特性,所以选用队列存储。
队列的定义:
[cpp] view plaincopy print?
- #define MAX 1000
- typedef struct seqqueue{
- bintree data[MAX];
- int front;
- int rear;
- }seqqueue;
- void enter(seqqueue *q,bintree t){
- if(q->rear == MAX){
- printf("the queue is full!\n");
- }else{
- q->data[q->rear] = t;
- q->rear++;
- }
- }
- bintree del(seqqueue *q){
- if(q->front == q->rear){
- return NULL;
- }else{
- q->front++;
- return q->data[q->front-1];
- }
- }
遍历实现
[cpp] view plaincopy print?
- void level_tree(bintree t){
- seqqueue q;
- bintree temp;
- q.front = q.rear = 0;
- if(!t){
- printf("the tree is empty\n");
- return ;
- }
- enter(&q,t);
- while(q.front != q.rear){
- t=del(&q);
- printf("%c ",t->data);
- if(t->lchild){
- enter(&q,t->lchild);
- }
- if(t->rchild){
- enter(&q,t->rchild);
- }
- }
- }
5、利用前序遍历的结果生成二叉树
[cpp] view plaincopy print?
- //递归调用,不存点,想的时候只关注于一个点,因为还会回来的,不要跟踪程序运行,否则容易多加循环
- void createtree(bintree *t){
- datatype c;
- if((c=getchar()) == '#')
- *t = NULL;
- else{
- *t = (bintree)malloc(sizeof(BinNode));
- (*t)->data = c;
- createtree(&(*t)->lchild);
- createtree(&(*t)->rchild);
- }
- }
6、二叉树的查找
[cpp] view plaincopy print?
- bintree search_tree(bintree t,datatype x){
- if(!t){
- return NULL;
- }
- if(t->data == x){
- return t;
- }else{
- if(!search_tree(t->lchild,x)){
- return search_tree(t->rchild,x);
- }
- return t;
- }
- }
7、统计结点个数
[cpp] view plaincopy print?
- int count_tree(bintree t){
- if(t){
- return (count_tree(t->lchild)+count_tree(t->rchild)+1);
- }
- return 0;
- }
8、比较两个树是否相同
[cpp] view plaincopy print?
- int is_equal(bintree t1,bintree t2){
- if(!t1 && !t2){ //都为空就相等
- return 1;
- }
- if(t1 && t2 && t1->data == t2->data){ //有一个为空或数据不同就不判断了
- if(is_equal(t1->lchild,t2->lchild))
- if(is_equal(t1->rchild,t2->rchild)){
- return 1;
- }
- }
- return 0;
- }
9、求二叉树的深度
[cpp] view plaincopy print?
- int hight_tree(bintree t){
- int h,left,right;
- if(!t){
- return 0;
- }
- left = hight_tree(t->lchild);
- right = hight_tree(t->rchild);
- h = (left>right?left:right)+1;
- return h;
- }
二叉树的前序、中序、后序遍历及互相求法相关推荐
- java中二叉树_Java工程师面试1000题224-递归非递归实现二叉树前、中、后序遍历...
224.使用递归和非递归实现二叉树的前.中.后序遍历 使用递归来实现二叉树的前.中.后序遍历比较简单,直接给出代码,我们重点讨论非递归的实现. class Node { public int valu ...
- C++实现二叉树 前、中、后序遍历(递归与非递归)非递归实现过程最简洁版本
本文并非我所写,是复制的该链接中的内容: 最近学习二叉树,想编程实现递归和非递归的实现方式: 递归的方式就不说了,因为大家的递归程序都一样:但是对于非递归的实现方式, 根据这几天的查阅资料已看到差不多 ...
- 【LeetCode | 二叉树前、中、后序遍历{迭代法}实现】
1.前序遍历 // 解题思路:利用栈的原理实现以迭代方法来前序遍历(根左右)二叉树 class Solution { public:vector<int> preorderTraversa ...
- java数据结构学习笔记-二叉树前、中、后序遍历
public class BinaryTreeDemo {public static void main(String args[]){Employee emp1= new Employee(1,&q ...
- 【LeetCode | 二叉树前、中、后序遍历{递归法}实现】
1.前序遍历 #include <iostream> #include <vector> #include <queue> #include <algorit ...
- 二叉树前、中、后序线索化及遍历
public class ThreadedBinaryTree {public static void main(String[] args){Heronodes node1=new Heronode ...
- 二叉树的前序中序后序遍历
二叉树的前序中序后序遍历 二叉树的遍历 前序遍历 中序遍历 后序遍历 总结 二叉树的遍历 二叉树的遍历有前序遍历,中序遍历,后序遍历三种. 今天我把二叉树的遍历方法给大家总结一下,也算对我自己学习的一 ...
- 【二叉树Java】二叉树遍历前序中序后序遍历的非递归写法
本文主要介绍二叉树前序中序后序遍历的非递归写法 在探讨如何写出二叉树的前序中序后序遍历代码之前,我们先来明确一个问题,前序中序后序遍历根据什么区分? 二叉树的前序中序后序遍历,是相较根节点说的.最先遍 ...
- 二叉树的前序中序后序遍历java代码实现
1.前序遍历概述 前序遍历(VLR) 是二叉树遍历的一种,也叫做先根遍历.先序遍历.前序周游,可记做根左右.前序遍历首先访问根结点然后遍历左子树,最后遍历右子树. 若二叉树为空则结束返回,否则: (1 ...
- 二叉树遍历(递归实现前序/中序/后序遍历)
1. 准备工作 我们先定义一棵普通的二叉树,如下图 2. 前序遍历 通过递归进行遍历: 如果二叉树为空,则操作返回: 如果非空,否则从根结点开始,然后遍历左子树,再遍历右子树. 前序遍历的结果是:AB ...
最新文章
- DFS+剪枝 hdu 5113 Black And White
- vue移动端过渡动画_Vue.js实现微信过渡动画左右切换效果
- [java进阶]3.slf4j作用及其实现原理
- 部署zabbix企业监控平台
- 文档根元素 project 必须匹配 doctype 根 null_快评:全新MG5上市6.49万起,但买它必须准备10万?...
- CSS - 层叠特性
- Android 应用开发(18)---在运行时请求权限
- 计算机级用英语怎么说,计算机国家一级用英语怎么说
- flutter安装教程(win7)
- ElasticSearch全文搜索引擎之入门以及环境搭建
- STM8L低功耗停止看门狗
- 通用对话框Dialog
- 路由器登陆192.168.1.1打开后出现移动登陆页面
- 固定偏置放大电路为何不能保证静态工作点的稳定性?
- 神奇的夏时令——本来设置好的日期在保存完成后少了一天?
- GYM 101173 K.Key Knocking(构造)
- 豆瓣电影分析报告:大陆和港台到底差(cha)在哪里?
- 【Codecs系列】X264码率控制总结1——ABR,CQP,CRF
- mercurymw305r虚拟服务器,水星MW305R(V4-V7)路由器设置教程 | 192路由网
- 电视机防近视预警系统c语言,预防近视、提升视力 TCL儿童电视亮相重庆
热门文章
- Yii2在部署新机器时需要注意的环境问题
- .net企业级架构实战之1——框架综述
- SQL必知必会-约束
- python的print怎么输出utf-8的编码_原创反转精度算法:小数的终极编码
- 电压放大倍数公式运放_模电的半壁江山——运算放大器的原理和应用
- 美女DBA带你了解PostgreSQL用户及角色
- 云端之战:Thomas Kurian离职,Java 11趋向收费,Ellison豪赌ERP和云数据库
- 如何构建尽可能小的容器镜像?
- 数据库流行度6月排行榜:Oracle飙升MySQL止跌回升
- 带你了解Typescript的14个基础语法