数据结构和算法学习记录——层序遍历(层次遍历)、二叉树遍历的应用(输出二叉树中的叶节点、求二叉树的高度、二元运算表达式树及其遍历、由两种遍历序列确定二叉树)

简介: 数据结构和算法学习记录——层序遍历(层次遍历)、二叉树遍历的应用(输出二叉树中的叶节点、求二叉树的高度、二元运算表达式树及其遍历、由两种遍历序列确定二叉树)

层序遍历

层序遍历可以通过一个队列来实现,其基本过程为:

先根节点入队,然后:

  1. 从队列中取出一个元素;
  2. 访问该元素所指的节点;
  3. 若该元素所指节点的左、右孩子节点非空, 则将其左、右孩子的指针顺序入队。
  4. 循环123的步骤,直到队列为空。

思路图解

代码实现

void LevelOrderTraversal(BinTree BT)
{
  Queue Q;
  BinTree T;
  if (!BT)
  {
    return; //若为空树则直接返回
  }
  Q = CreateQueue(); //创建并初始化队列Q
  Add(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);
  }
}

求二叉树的高度

树是递归定义的,一颗二叉树的高度应该等于左右两颗子树的最大高度+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;                            //空树的高度为0
  }
}

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

对上面的表达式树进行三种遍历,可以得到三种不同的访问结果:

试着分别写出上面表达式树前序中序和后序遍历的不同表达式,复习一遍之前讲的树的遍历。



先序遍历可以得到前缀表达式:++a*bc*+*defg


中序遍历可以得到中缀表达式:a+b*c+d*e+f*g


后序遍历可以得到后缀表达式:abc*+de*f+g*+


但需要注意的是:中缀表达式会受到运算符优先级的影响,所以单单这样通过中序遍历得出的中缀表达式是不完全准确的。

解决方法是:在输出左子树之前,先输出一个左括号,左子树结束的时候再输出一个右括号。

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

已知三种遍历中的任意两种遍历序列,能否唯一确定一颗二叉树呢?

答案是:两种遍历序列中,必须要有一种是中序遍历才能够唯一确定一颗二叉树

假设没有中序,看下面两个序列:

先序遍历序列:A B

后序遍历序列:B A

像这样一组简单的序列,只有先序遍历序列和后序遍历序列的情况下,就有两颗是符合的二叉树,其中根节点是容易确定的,先序的第一个节点就是根,后序的最后一个节点就是根;但是左右节点是不好区分的,所以就导致了只有先序序列和后序序列的情况下没法唯一地确认一颗二叉树。


下面就来看看,已知先序序列和中序序列,怎么样来确定一颗二叉树。

思路:

  1. 根据先序遍历序列第一个节点确定根节点;
  2. 根据根节点在中序遍历序列中分割出左右两个子序列;
  3. 对左子树和右子树分别递归使用相同的方法继续分解。


举个例子清晰一下思路:

先序序列: abcdefghij

中序序列: cbedahgijf

所以最终通过先序遍历序列和中序遍历序列唯一确定的二叉树就为:


end



目录
相关文章
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
1264 77
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
574 10
 算法系列之数据结构-二叉树
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
518 3
 算法系列之数据结构-Huffman树
|
存储 自然语言处理 数据库
【数据结构进阶】AVL树深度剖析 + 实现(附源码)
在深入探讨了AVL树的原理和实现后,我们不难发现,这种数据结构不仅优雅地解决了传统二叉搜索树可能面临的性能退化问题,还通过其独特的平衡机制,确保了在任何情况下都能提供稳定且高效的查找、插入和删除操作。
1041 19
|
存储 C++
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
696 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
393 10
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
1400 10
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
495 59
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
339 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。

热门文章

最新文章