0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

二叉树的前序遍历非递归实现

算法与数据结构 来源:袁厨的算法小屋 作者:袁厨的算法小屋 2021-05-28 13:59 次阅读

我们之前说了二叉树基础及二叉的几种遍历方式及练习题,今天我们来看一下二叉树的前序遍历非递归实现。

前序遍历的顺序是, 对于树中的某节点,先遍历该节点,然后再遍历其左子树,最后遍历其右子树。

我们先来通过下面这个动画复习一下二叉树的前序遍历。

迭代遍历

我们试想一下,之前我们借助队列帮我们实现二叉树的层序遍历,

那么可不可以,也借助数据结构,帮助我们实现二叉树的前序遍历。

假设我们的二叉树为 [1,2,3]。我们需要对其进行前序遍历。其遍历顺序为

当前节点 1,左孩子 2,右孩子 3。

这里可不可以用栈,帮我们完成前序遍历呢?

栈和队列的那些事

我们都知道栈的特性是先进后出,我们借助栈来帮助我们完成前序遍历的时候。

则需要注意的一点是,我们应该先将右子节点入栈,再将左子节点入栈。

这样出栈时,则会先出左节点,再出右子节点,则能够完成树的前序遍历。

我们用一句话对上图进行总结,当栈不为空时,栈顶元素出栈,如果其右孩子不为空,则右孩子入栈,其左孩子不为空,则左孩子入栈。还有一点需要注意的是,我们和层序遍历一样,需要先将 root 节点进行入栈,然后再执行 while 循环。

看到这里你已经能够自己编写出代码了,不信你去试试。

时间复杂度:O(n) 需要对所有节点遍历一次

空间复杂度:O(n) 栈的开销,平均为 O(logn) 最快情况,即斜二叉树时为 O(n)

参考代码

class Solution {

public List《Integer》 preorderTraversal(TreeNode root) {

List《Integer》 list = new ArrayList《》();

Stack《TreeNode》 stack = new Stack《》();

if (root == null) return list;

stack.push(root);

while (!stack.isEmpty()) {

TreeNode temp = stack.pop();

if (temp.right != null) {

stack.push(temp.right);

}

if (temp.left != null) {

stack.push(temp.left);

}

//这里也可以放到前面

list.add(temp.val);

}

return list;

}

}

Morris

Morris 遍历利用树的左右孩子为空(大量空闲指针),实现空间开销的极限缩减。这个遍历方式,稍微有那么一丢丢难理解,不过结合动图,也就一目了然,下面我们先看动画吧。

看完视频,是不是感觉自己搞懂了,又感觉自己没搞懂,哈哈,咱们继续往下看。

我们之前说的,Morris 遍历利用了树中大量空闲指针的特性,我们需要找到当前节点的左子树中的最右边的叶子节点,将该叶子节点的 right 指向当前节点。例如当前节点为2,其左子树中的最右节点为 9 ,则在 9 节点添加一个 right 指针指向 2。

其实上图中的 Morris 遍历遵循两个原则,我们在动画中也能够得出。

1.当 p1.left == null 时,p1 = p1.right。(这也就是我们为什么要给叶子节点添加 right 指针的原因)

2.如果 p1.left != null,找到 p1 左子树上最右的节点。(也就是我们的 p2 最后停留的位置),此时我们又可以分为两种情况,一种是叶子节点添加 right 指针的情况,一种是去除叶子节点 right 指针的情况。

如果 p2 的 right 指针指向空,让其指向 p1,p1向左移动,即 p1 = p1.left

如果 p2 的 right 指针指向 p1,让其指向空,(为了防止重复执行,则需要去掉 right 指针)p1 向右移动,p1 = p1.right。

这时你可以结合咱们刚才提到的两个原则,再去看一遍动画,并代入规则进行模拟,差不多就能完全搞懂啦。

下面我们来对动画中的内容进行拆解

首先 p1 指向 root节点

p2 = p1.left,下面我们需要通过 p2 找到 p1的左子树中的最右节点。即节点 5,然后将该节点的 right 指针指向 root。并记录 root 节点的值。

向左移动 p1,即 p1 = p1.left

p2 = p1.left ,即节点 4 ,找到 p1 的左子树中的最右叶子节点,也就是 9,并将该节点的 right 指针指向 2。

继续向左移动 p1,即 p1 = p1.left,p2 = p1.left。也就是节点 8。并将该节点的 right 指针指向 p1。

我们发现这一步给前两步是一样的,都是找到叶子节点,将其 right 指针指向 p1,此时我们完成了添加 right 指针的过程,下面我们继续往下看。

我们继续移动 p1 指针,p1 = p1.left。p2 = p.left。此时我们发现 p2 == null,即下图此时我们需要移动 p1, 但是不再是 p1 = p1.left 而是 p1 = p1.right。也就是 4,继续让 p2 = p1.left。此时则为下图这种情况

此时我们发现 p2.right != null 而是指向 4,说明此时我们已经添加过了 right 指针,所以去掉 right 指针,并让 p1 = p1.right

下面则继续移动 p1 ,按照规则继续移动即可,遇到的情况已经在上面做出了举例,所以下面我们就不继续赘述啦,如果还不是特别理解的同学,可以再去看一遍动画加深下印象。时间复杂度 O(n),空间复杂度 O(1)下面我们来看代码吧。

代码

class Solution {

public List《Integer》 preorderTraversal(TreeNode root) {

List《Integer》 list = new ArrayList《》();

if (root == null) {

return list;

}

TreeNode p1 = root; TreeNode p2 = null;

while (p1 != null) {

p2 = p1.left;

if (p2 != null) {

//找到左子树的最右叶子节点

while (p2.right != null && p2.right != p1) {

p2 = p2.right;

}

//添加 right 指针,对应 right 指针为 null 的情况

if (p2.right == null) {

list.add(p1.val);

p2.right = p1;

p1 = p1.left;

continue;

}

//对应 right 指针存在的情况,则去掉 right 指针

p2.right = null;

} else {

list.add(p1.val);

}

//移动 p1

p1 = p1.right;

}

return list;

}

}

原文标题:把二叉树揉碎(二)

文章出处:【微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 二叉树
    +关注

    关注

    0

    文章

    74

    浏览量

    12322

原文标题:把二叉树揉碎(二)

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    什么是默克尔(Merkle Tree)?如何计算默克尔根?

    01 默克尔的概念 默克尔(Merkle Tree)是一种特殊的二叉树,它的每个节点都存储了一个数据块的哈希值。哈希值是一种可以将任意长度的数据转换为固定长度的字符串的算法,它具有唯一性和不可
    的头像 发表于 09-30 18:22 767次阅读
    什么是默克尔<b class='flag-5'>树</b>(Merkle Tree)?如何计算默克尔根?

    Python递归的经典案例

    当我们碰到诸如需要求阶乘或斐波那契数列的问题时,使用普通的循环往往比较麻烦,但如果我们使用递归时,会简单许多,起到事半功倍的效果。这篇文章主要和大家分享一些和递归有关的经典案例,结合一些资料谈一下个人的理解,也借此加深自己对递归
    的头像 发表于 08-05 15:57 319次阅读

    递归神经网络和循环神经网络的模型结构

    递归神经网络是一种旨在处理分层结构的神经网络,使其特别适合涉及树状或嵌套数据的任务。这些网络明确地模拟了层次结构中的关系和依赖关系,例如语言中的句法结构或图像中的层次表示。它使用递归操作来分层处理信息,有效地捕获上下文信息。
    的头像 发表于 07-10 17:21 634次阅读
    <b class='flag-5'>递归</b>神经网络和循环神经网络的模型结构

    递归神经网络的实现方法

    (Recurrent Neural Network,通常也简称为RNN,但在此处为区分,我们将循环神经网络称为Recurrent RNN)不同,递归神经网络更侧重于处理树状或图结构的数据,如句法分析、自然语言的语法结构等。以下将从递归
    的头像 发表于 07-10 17:02 310次阅读

    指电极上覆盖敏感材料的阻值计算

    覆盖的敏感材料厚度超出指厚度时计算电阻,是否可以视作指电极指间电阻多个周期串联后与超出指厚度部分敏感材料电阻并联
    发表于 07-05 14:48

    递归神经网络结构形式主要分为

    结构形式。 Elman网络 Elman网络是一种基本的递归神经网络结构,由Elman于1990年提出。其结构主要包括输入层、隐藏层和输出层,其中隐藏层具有时间延迟单元,可以存储前一时刻的隐藏状态。Elman网络的基本原理是将前一时刻的隐藏状态作为当前时刻的额外输入,从而实现
    的头像 发表于 07-05 09:32 508次阅读

    递归神经网络与循环神经网络一样吗

    递归神经网络(Recursive Neural Network,RvNN)和循环神经网络(Recurrent Neural Network,RNN)是两种不同类型的神经网络结构,它们在处理序列数据
    的头像 发表于 07-05 09:28 806次阅读

    递归神经网络主要应用于哪种类型数据

    处理(NLP) 自然语言处理是递归神经网络最重要的应用领域之一。在NLP中,递归神经网络可以用于以下任务: 1.1 语言模型(Language Modeling) 语言模型是预测给定词序列中下一个词的概率分布。递归神经网络可以捕
    的头像 发表于 07-04 14:58 656次阅读

    递归神经网络是循环神经网络吗

    递归神经网络(Recurrent Neural Network,简称RNN)和循环神经网络(Recurrent Neural Network,简称RNN)实际上是同一个概念,只是不同的翻译方式
    的头像 发表于 07-04 14:54 718次阅读

    递归神经网络的结构、特点、优缺点及适用场景

    递归神经网络(Recurrent Neural Networks,简称RNN)是一种具有循环结构的神经网络,其核心特点是能够处理序列数据,并对序列中的信息进行记忆和传递。RNN在自然语言处理、语音
    的头像 发表于 07-04 14:52 1274次阅读

    错误解决

    重入VI中无法进行递归调用。表示重入VI无法将本身作为子VI调用。如需实现递归调用,周期中全部VI必须可重入并且至少存在一个动态分配或
    发表于 03-07 11:28

    关于C语言中的递归

    递归指的是在函数的定义中使用函数自身的方法。
    发表于 02-26 10:34 375次阅读
    关于C语言中的<b class='flag-5'>递归</b>

    荣小菜补钙记第61期_LabVIEW之递归文件及文件夹

    深入层级遍历,直到不再出现文件夹。While循环最终运行次数反映了最大层级。 4. 遍历全部层级文件及文件夹_递归文件列表函数 除了自己写代码实现该功能,LabVIEW也提供了“
    发表于 02-16 21:36

    哈夫曼编码怎么算 哈夫曼编码左边是0还是1

    二叉树,将出现频率高的字符用较短的编码表示,而出现频率低的字符则用较长的编码表示。通过这种方式,可以实现对数据进行高效的编码和解码。 下面我们将详细介绍哈夫曼编码的算法过程。 统计字符频率 在进行哈夫曼编码前,首先需
    的头像 发表于 01-30 11:27 2823次阅读

    一种面向标识公共递归解析节点的数据安全加固策略

    摘要 :为解决工业互联网标识解析体系公共递归解析节点信息透明、缺乏隐私数据保护和身份权限管理等问题,提出了一种面向标识公共递归解析节点的数据安全加固策略。通过设计加密机制及细粒度权限查验机制,实现了标识解析
    的头像 发表于 12-26 11:27 672次阅读
    一种面向标识公共<b class='flag-5'>递归</b>解析节点的数据安全加固策略