本次笔记内容:
3.3.1 先序中序后序遍历
3.3.2 中序非递归遍历
3.3.3 层序遍历
3.3.4 遍历应用例子
小白专场:题意理解及二叉树表示
小白专场:程序框架、建树及同构判别

文章目录

  • 二叉树的三种基本遍历
    • 先序遍历
    • 中序遍历
    • 后序遍历
    • 特点
  • 中序非递归遍历(使用堆栈)
    • 中序堆栈遍历
    • 先序也能非递归遍历
  • 层序遍历
    • 队列实现
  • 遍历二叉树的应用
    • 输出二叉树中的叶子结点
    • 二叉树的高度
    • 二元运算表达式树及其遍历
    • 由两种遍历序列确定二叉树
      • 先序和中序来确定一棵二叉树分析
  • 树的同构
    • 题意理解
    • 输入格式
    • 二叉树表示
    • 程序框架搭建
    • 如何建立二叉树
    • 判断同构

二叉树的三种基本遍历

先序遍历

遍历过程为:

  1. 访问根节点;
  2. 先序遍历其左子树;
  3. 先序遍历其右子树。
void PreOrderTraversal(BinTree BT)
{if (BT){printf("%d", BT->Data);PreOrderTraversal(BT->Left) ;PreOrderTraversal(BT->Right);}
}

如上,使用递归实现。

效果如上图:A(BDFE)(CGHI)。

中序遍历

遍历过程为:

  1. 中序遍历其左子树;
  2. 访问根节点;
  3. 中序遍历其右子树。
void InOrderTraversal(BinTree BT)
{if (BT){PreOrderTraversal(BT->Left) ;printf("%d", BT->Data);PreOrderTraversal(BT->Right);}
}

效果如上图。

后序遍历

遍历过程为:

  1. 后序遍历其左子树;
  2. 后序遍历其右子树;
  3. 访问根节点。
void PostOrderTraversal(BinTree BT)
{if (BT){PreOrderTraversal(BT->Left);PreOrderTraversal(BT->Right);printf("%d", BT->Data);}
}

效果如上图。

特点

三种遍历过程中路径一样,只是访问各结点的时机不同。

如上图,对于B结点,所谓先序,就是第一次碰到它就print,中序就是第二次碰到就print,后续是第三次碰到才print。

中序非递归遍历(使用堆栈)

中序堆栈遍历

如上图,沿着路径行进,第二次碰到在堆栈中的元素,则抛出堆栈中元素。

  • 碰到一个结点就把它压栈,并去遍历它的左子树;
  • 当左子树遍历结束后,从栈顶弹出这个结点并访问它;
  • 然后按其右指针再去中序遍历该结点的右子树。
void InOrderTraversal(BinTree BT)
{BinTree T = BT;Stack S = CreateStack(MaxSize); /* 创建并初始化堆栈S */while (T || !IsEmpty(S)){while (T) /* 一直向左并将沿途节点压入堆栈 */{Push(S, T);T = T->Left;}if (!IsEmpty(S)){T = Pop(S);             /* 结点弹出堆栈 */printf("%5d", T->Data); /* (访问)打印结点 */T = T->Right;           /* 转向右子树 */}}
}

先序也能非递归遍历

void PreOrderTraversal(BinTree BT)
{BinTree T = BT;Stack S = CreateStack(MaxSize); /* 创建并初始化堆栈S */while (T || !IsEmpty(S)){while (T) /* 一直向左并将沿途节点压入堆栈 */{printf("%5d", T->Data); /* (访问)打印结点 */Push(S, T);T = T->Left;}if (!IsEmpty(S)){T = Pop(S);             /* 结点弹出堆栈 */T = T->Right;           /* 转向右子树 */}}
}

如上,改变printf(访问)执行时机即可。

后序遍历也可以用堆栈实现。

层序遍历

二叉树遍历的核心问题:二维结构的线性化。

  • 从结点访问其左、右儿子结点;
  • 访问左儿子后,右儿子结点怎么办?
    • 需要一个存储结构保存暂时不访问的结点;
    • 存储结构:堆栈、队列。

队列实现

遍历从根结点开始,首先将根结点入队,然后开始执行循环:结点出队、访问该结点、其左右儿子入队。

根结点入队,然后:

  1. 从队列中取出一个元素;
  2. 访问该元素所指结点;
  3. 若该元素所指结点的左、右孩子结点非空,则将其左、右孩子的指针顺序入队。
void LevelOrderTraversal(BinTree BT)
{Queue Q;if (!BT)return;Q = CreateQueue(MaxSize);AddQ(Q, BT);while (!IsEmptyQ(Q)){T = DeleteQ(Q);printf("%d\n", T->Data);if (T->Left)AddQ(Q, T->Left);if (T->Right)AddQ(Q, T->Right);}
}

遍历二叉树的应用

输出二叉树中的叶子结点

void PreOrderPrintLeaves(BinTree BT)
{if (BT){if (!BT->Left && !BT->Right)printf("%d", BT->Data);PreOrderPrintLeaves(BT->Left);PreOrderPrintLeaves(BT->Right);}
}

如上,在printf()之前加上一个if()判断是否为叶子结点。

二叉树的高度

如上图,首先应明确左右子树高度,加上1,为树高度这条结论。

int PostOrderGetHeight(BinTree BT)
{int HL, HR, MaxH;if (BT){HL = PostOrderGetHeight(BT->Left);HR = PostOrderGetHeight(BT->Right);MaxH = (HL > HR) ? HL : HR;return (MaxH + 1);}else{return 0;}
}

二元运算表达式树及其遍历

如上图,叶结点是运算树;不同遍历方式得到不同缀表达式。中缀表达式会受到运算优先级的影响(不准),其他表达式准。

中缀表达式解决办法:输出左子树时,先出个左括号,输出右子树后,出个右括号。

由两种遍历序列确定二叉树

必须有中序遍历才行!

没有中序的困扰,如

  • 先序遍历序列:A B;
  • 后续遍历序列:B A。

得到如上图,不能唯一确定二叉树。

先序和中序来确定一棵二叉树分析
  • 根据先序遍历序列第一个结点确定根节点;
  • 根据根节点在中序遍历序列中分割出左右两个子序列;
  • 对左子树和右子树分别递归使用相同的方法继续分解。

如上图,先序、中序遍历结果的结构不同。因此可以根据二者进行二叉树确定。

类似的,后序和中序遍历序列也可以确定一棵二叉树。

树的同构

题意理解

给定两棵树T1和T2,如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的。

如上图,上面的两棵树同构,下面的不同构。

输入格式

后面的两个整数代表左右儿子是谁(编号,从0开始)。


如上图,这种输入方式下,二叉树的输入不一定将根节点放在第一个。

二叉树表示

使用结构数组表示二叉树,用静态链表来表示(左右儿子用近似链表的方式表示)。

#define MaxTree 10
#define ElementType char
#define Tree int // 从0开始的索引(结点标号)
#define Null -1  // Null在std.io中定义值为0,struct TreeNode
{ElementType Element;Tree Left;Tree Right;
} T1[MaxTree], T2[MaxTree];

程序框架搭建

int main() {建二叉树1建二叉树2判别是否同构并输出return 0;
}

需要设计的函数:

  • 读取数据建二叉树;
  • 二叉树的同构判别。
int main() {Tree R1, R2;R1 = BuildTree(T1);R2 = BuildTree(T2);if (Isomorphic(R1, R2)) printf("Yes\n");else printf("No\n");return 0;
}

如何建立二叉树

如上图,建立二叉树过程中,先读入结点个数。函数的返回值为树根。

Tree BuildTree(struct TreeNode T[])
{scanf("%d\n", N);if (N){for (i = 0; i < N; i++)check[i] = 0;for (i = 0; i < N; i++){scanf("%c %c %c\n", &T[i].Element, &cl, &cr);if (cl != '-'){T[i].Left = cl - '0'; // 字符串转换为intcheck[T[i].Left] = 1;}elseT[i].Left = Null;if (cr != '-'){T[i].Right = cr - '0';check[T[i].Right] = 1;}elseT[i].Right = Null;}for (i = 0; i < N; i++)if (!check[i])break;Root = i;}return Root;
}

判断同构

先考虑特殊情况,如是否都为空?是否一个空一个不为空?再考虑两树左子树根结点是否相同?是的话将左右子树递归;或者另一种情况,左子树根结点与右子树根结点是否相同?是的话左子树和右子树交叉比较递归。

int Isomorphic(Tree R1, Tree R2)
{if ((R1 == Null) && (R2 == Null))/* both empty */ retrun 1;if (((R1 == Null) && (R2 != Null)) || (R1 != Null) && (R2 == Null))/* one of them is empty */ return 0;if (T1[R1].Element != T2[R2].Element)/* roots are different */ return 0;if ((T1[R1].Left == Null) && (T2[R2].Left == Null))/* both have no left subtree */return Isomorphic(T1[R1].Right, T2[R2].Right);if (((T1[R1].Left != Null) && (T2[R2].Left != Null)) &&((T1[T1[R1].Left].Element) == (T2[T2[R2].Left].Element)))/* no need to swap the left and the right */return (Isomorphic(T1[R1].Left, T2[R2].Left) &&Isomorphic(T1[R1].Right, T2[R2].Right));else /* need to swap the left and the right */return (Isomorphic(T1[R1].Left, T2[R2].Right) &&Isomorphic(T1[R1].Right, T2[R2].Left));
}

实现如上。逻辑需要完整清楚。

【数据结构笔记10】二叉树的先序、中序、后序遍历,中序遍历的堆栈/非递归遍历算法,层序遍历,确定一个二叉树,树的同构相关推荐

  1. 二叉树的遍历:先序 中序 后序遍历的递归与非递归实现及层序遍历

    二叉树的定义:一种基本的数据结构,是一种每个节点的儿子数目都不多于2的树 树节点的定义如下: // 树(节点)定义 struct TreeNode {int data; // 值TreeNode* l ...

  2. 二叉树前中后序遍历的非递归实现以及层次遍历、zig-zag型遍历详解

    前言 二叉树的遍历是一个比较常见的问题,递归实现二叉树的前中后序遍历比较简单,但非递归实现二叉树的前中后序遍历相对有难度.这篇博客将详述如何使用非递归的方式实现二叉树的前中后序遍历,在进行理论描述的同 ...

  3. 二叉树的层序遍历,前序遍历(递归,非递归),中序遍历(递归,非递归),后续遍历(递归,非递归)

    文章目录 二叉树的层序遍历 前序遍历 递归版本 非递归版本 中序遍历 递归版本 非递归版本 后序遍历 递归版本 非递归版本 二叉树的层序遍历 void printTree(BinaryTree* ar ...

  4. 【C语言】二叉树中序遍历(递归和非递归)算法

    二叉树中序遍历的实现思想是: 访问当前节点的左子树: 访问根节点: 访问当前节点的右子树: 图 1 二叉树 以图  1 为例,采用中序遍历的思想遍历该二叉树的过程为: 访问该二叉树的根节点,找到 1: ...

  5. 后序遍历的非递归算法python_二叉树后序遍历(递归与非递归)算法C语言实现...

    二叉树后序遍历的实现思想是:从根节点出发,依次遍历各节点的左右子树,直到当前节点左右子树遍历完成后,才访问该节点元素. 图 1 二叉树 如图 1 中,对此二叉树进行后序遍历的操作过程为: 从根节点 1 ...

  6. 图解二叉树非递归版的前序遍历算法

    " 图解用栈数据结构对树的前序遍历,中序遍历,后续遍历." 树的遍历 所谓遍历 (Traversal) 是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问.访问结点所做 ...

  7. python随机产生10个数然后前5个升序后5个降序_编写程序,生成包含 20 个随机数的列表,然后将前 10 个元素升序排列,后 10 个元素降序排列,并输出结果。_学小易找答案...

    [填空题]在 Python3.x 中 input() 函数接收到的用户输入数据一律为 ________ . [填空题]运算符% (可以.不可以)对浮点数进行求余数操作. [简答题]解释 Python ...

  8. 二叉树的后序遍历的非递归实现算法

    算法思想: 后序遍历二叉树的顺序是左右根.我们需要用一个辅助栈来记录没有访问过得祖宗节点. 借助一个简单的二叉树模拟一下过程. 后序遍历的顺序是DEBFGCA 算法步骤 1.沿着根的左孩子,依次入栈, ...

  9. 二叉树:先序遍历,中序遍历,后序遍历,层序/层次遍历

    目录 二叉树 二叉树的递归遍历 先序递归遍历 中序递归遍历 后序递归遍历 二叉树的非递归遍历 先序遍历使用栈结构 中序遍历使用栈结构 后序遍历使用栈结构 中序遍历:(morris遍历)空间复杂度O(1 ...

最新文章

  1. 0-1背包使用一维dp数组时为何v要从大到小枚举
  2. 浏览器登录java_java – 如何停止已登录的用户从其他浏览器登录
  3. Windows下配置Chrome WebDriver
  4. PHP7扩展开发(二):配置项与全局数值
  5. Spring Cloud 服务注册与发现 [ eureka ]
  6. openvswitch安装与使用
  7. 【Linux】bash: groupadd: command not found a
  8. 自学python顺序-python数据结构学习之实现线性表的顺序
  9. Euraka启动记录
  10. 如何使用EXCEL连接数据库,获取数据
  11. IOS - 苹果微信不打开收不到新消息提醒怎么办?
  12. 电脑插上u盘计算机管理有显示,u盘在电脑上一直显示扫描怎么办
  13. Android之shape属性设置
  14. tensorflow----tensorboard之histogram与distributions
  15. 家里安装了两条宽带,有什么方法把两家运营商的宽带聚合起来用?
  16. 知识产权行业获客难?一招解决
  17. Hexo博客添加live2d卡通人物
  18. 【简易搭建个人博客】------- 基于BT面板的个人博客搭建
  19. 网络设备配置与管理,校园网规划
  20. gateway+vue实现防接口重放、防篡改

热门文章

  1. ajax--跨域问题及三种简单的解决方案
  2. MyBatis 插入失败后爆出 500 ,如何捕获异常?
  3. sqlserver数据导入hdfs和hive的解决方案
  4. 【UE4游戏开发】安装UE4时报SU-PQR1603错误的解决方法
  5. flutter initializing gradle终极解决方案
  6. 怎么解决docker pull拉取镜像速度过慢的问题
  7. npm突然找不到D:\nodejs\node_modules\npm\bin\npm-cli.js的解决方法
  8. 遇见Python.h: No such file or directory的解决方法
  9. git使用报错: fatal: Couldn‘t find remote ref master的解决方法
  10. 异常:org.springframework.http.converter.HttpMessageNotReadableException