树的遍历

【数据结构】树的遍历、森林的遍历 树的遍历是树的一种重要的运算。所谓遍历是指对树中所有结点的信息的访问,即依次对树中每个结点访问一次且仅访问一次。 树可被看成是由树的根结点和根结点的所有子树所构成的森林两部分组成。 ​ 阅读详情

树的遍历


    树的遍历在笔试的选择中出现的概率还是比较大的,特别的由两个遍历序列得到一个唯一的树这种题,所以掌握好遍历这部分还是挺重要的。

一、树的节点数问题

    数的节点数问题有时候也会问到,比较简单基础

  • 1.非空二叉树的第N层最大有2^(N-1)个节点
  • 2.高度为H的二叉树最多有2^(H)-1个节点

二、树的四种遍历方式

    树有四种遍历方式:前序遍历、中序遍历、后序遍历、层次遍历

  • 1.前序遍历:根节点–>左节点–>右节点
  • 2.中序遍历:左节点–>根节点–>右节点
  • 3.后序遍历:左节点–>右节点–>根节点
  • 4.层次遍历:按照树的层输出

三、树的还原

    树可以根据前序、中序和、后序的组合还原成唯一的树

  • 1.前序+中序:根据前序得到根节点,然后根据中序得到根节点的左右节点
  • 2.后序+中序:根据后序得到根节点,然后根据中序得到根节点的左右节点
  • 3.前序+后序:能得到根节点,但无法确定左右节点,所以无法得到唯一的树

四、树的变量实现代码

    下面是自己写的一个支持基本类型的的模板函数,可以进行树的前序、中序和后序遍历,到现在我们可以知道树的前序、中序和后序的前中后的含义其实是根节点的输出位置而言的,这样就能简单记住了

#pragma once
#ifndef __MYNODE_
#define __MYNODE_

#include <iostream>
#include <vector>

using namespace std;

template<class T>
struct TreeNode {
    T val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(T x) :
        val(x), left(NULL), right(NULL) {
    }

    //树的前序遍历
    static void preorder(TreeNode<T>* root, vector<T> &v) {
        v.push_back(root->val);
        if (root->left != NULL) {
            preorder(root->left, v);
        }
        if (root->right != NULL) {
            preorder(root->right, v);
        }
    }
    static void preorder(TreeNode<T>* root) {
        std::cout << root->val << endl;
        if (root->left != NULL) {
            preorder(root->left);
        }
        if (root->right != NULL) {
            preorder(root->right);
        }
    }

    //树的中序遍历
    static void inorder(TreeNode<T>* root, vector<T> &v) {
        if (root->left != NULL) {
            inorder(root->left, v);
        }
        v.push_back(root->val);
        if (root->right != NULL) {
            inorder(root->right, v);
        }
    }
    static void inorder(TreeNode<T>* root) {
        if (root->left != NULL) {
            inorder(root->left);
        }
        std::cout << root->val << endl;
        if (root->right != NULL) {
            inorder(root->right);
        }
    }

    //树的后序遍历
    static void postorder(TreeNode<T>* root, vector<T> &v) {
        if (root->left != NULL) {
            postorder(root->left, v);
        }
        if (root->right != NULL) {
            postorder(root->right, v);
        }
        v.push_back(root->val);
    }
    static void postorder(TreeNode<T>* root) {
        if (root->left != NULL) {
            postorder(root->left);
        }
        if (root->right != NULL) {
            postorder(root->right);
        }
        std::cout << root->val << endl;
    }
};
#endif // !__MYNODE_

五、Morris遍历方法

    现在向大家介绍一个遍历二叉树的神级方法,这个方法速度比递归快,而且不需要其他的存储结构。遍历的总体只有两个:

  • 1.开始时位于子树的头节点,使其左子树的最右节点的空节点指向它,然后向它的左子树移动。如果其左子树存在左儿子,重复步骤1;如果不存在,进行步骤2
  • 2.向其右子树移动,判断移动到的节点的左子树的最右节点的空指针是否指向当前移动到的节点,如果是,则将左子树的最右节点指向空。接下来重复步骤2.如果当前节点的左子树的最右节点不是指向它,则进行步骤1。
  • 3.如果当前移动的节点为空则结束

    下面是各种实验Morris方法的前序、中序和后序代码,其中后序遍历比较麻烦,由于本文多是总结和展示,各位想深入了解可以看后面的参考链接和推荐书籍

//Morris前序遍历
    static void OrderMorris(TreeNode<T>* root) {
        if (root == NULL) return;

        TreeNode* cur = root;
        TreeNode* temp;
        while (cur != NULL) {
            temp = cur->left;
            //如果其存在左子树,让其左子树的最右节点指向它或为空
            if (temp != NULL) {
                //找到其最右节点
                while (temp->right != NULL && temp->right != cur) {
                    temp = temp->right;
                }
                //如果指向子树根节点则让其指向空,并且当前指针向左子树移动
                if (temp->right == NULL) {
                    temp->right = cur;
                    cout << cur->val << ' ';
                    cur = cur->left;
                }
                //如果指向当前根节点则让其指向空
                else {
                    temp->right = NULL;
                    cur = cur->right;
                }
            }
            //当前节点不存在左子树则向右移动
            else {
                cout << cur->val << ' ';
                cur = cur->right;
            }
        }
        cout << endl;
    }

    //Morris中序遍历
    static void InMorris(TreeNode<T>* root) {
        if (root == NULL) return;

        TreeNode* cur = root;
        TreeNode* temp;
        while (cur != NULL) {
            temp = cur->left;
            //如果其存在左子树,让其左子树的最右节点指向它或为空
            if (temp != NULL) {
                //找到其最右节点
                while (temp->right != NULL && temp->right != cur) {
                    temp = temp->right;
                }
                //如果指向子树根节点则让其指向空,并且当前指针向左子树移动
                if (temp->right == NULL) {
                    temp->right = cur;
                    cur = cur->left;
                }
                //如果指向当前根节点则让其指向空
                else {
                    temp->right = NULL;
                    cout << cur->val << ' ';
                    cur = cur->right;
                }
            }
            //当前节点不存在左子树则向右移动
            else {
                cout << cur->val << ' ';
                cur = cur->right;
            }
        }
        cout << endl;
    }

    //Morris后序遍历
    static void PostMorris(TreeNode<T>* root) {
        if (root == NULL) return;

        TreeNode* cur = root;
        TreeNode* temp;
        while (cur != NULL) {
            temp = cur->left;
            //如果其存在左子树,让其左子树的最右节点指向它或为空
            if (temp != NULL) {
                //找到其最右节点
                while (temp->right != NULL && temp->right != cur) {
                    temp = temp->right;
                }
                //如果指向子树根节点则让其指向空,并且当前指针向左子树移动
                if (temp->right == NULL) {
                    temp->right = cur;
                    cur = cur->left;
                }
                //如果指向当前根节点则让其指向空
                else {
                    temp->right = NULL;
                    //在第二次到此节点时逆序打印
                    InvertedPrint(cur->left);
                    cur = cur->right;
                }
            }
            //当前节点不存在左子树则向右移动
            else {
                cur = cur->right;
            }
        }
        //最后逆序打印根节点的右部分
        InvertedPrint(root);
        cout << endl;
    }
    //为了配合Morris后序遍历而生的链表逆序打印
    static void InvertedPrint(TreeNode* head) {
        if (head == NULL) return;

        TreeNode *cur, *next, *nnp;
        cur = head;
        next = cur->right;
        if (next == NULL) {
            cout << cur->val << ' ';
            return;
        }
        nnp = next->right;
        if (nnp == NULL) {
            cout << next->val << ' ' << cur->val << ' ';
            return;
        }

        while (cur->right != NULL) {
            next->right = cur;
            cur = next;
            next = nnp;
            nnp = next->right;
            if (nnp == NULL) {
                next->right = cur;
                cur = next;
                break;
            }
        }

        next = cur->right;
        nnp = next->right;
        cur->right = NULL;
        while (true) {
            cout << cur->val << ' ';
            next->right = cur;
            cur = next;
            next = nnp;
            if (nnp->right == cur) {
                cout << cur->val << ' ' << next->val << ' ';
                break;
            }
            nnp = next->right;
        }
    }

参考链接

数据结构基础 各种遍历还原二叉树

数据结构(六)——二叉树 前序、中序、后序、层次遍历及非递归实现 查找、统计个数、比较、求深度的递归实现

Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间

推荐书籍

《程序员代码面试指南》–左程云(这是一本神书,突击的时候还是挺有用,其中的解题思想和技巧给人很多启发,左神的解说视频也是很给人启发,特别推荐给大家,里面有这个题目的详细解法)

[树] 二叉树、树、森林遍历问题 | 树的中序遍历问题 文章目录[总结] 二叉树、树、森林三者遍历比较树的中序遍历问题 [总结] 二叉树、树、森林三者遍历比较 【三种遍历方法对比】 二叉树 树 森林 先序遍历 第一次经过该结点就访问 根访问在前,先访问根结点,后访问其他结点 从左到右对森林中的每一棵树进行【先根遍历】 中序遍历 第二次经过结点的时候访问 树的度不一定,一般不说中序遍历,但非要谈,请看下面的分析 森林的中序、后序,只... 阅读详情

相关推荐

Dify使用教程(创建应用)

Dify的安装部署我已经写过了,简单的模型配置我也在前面进行了讲解,今天我们主要来讲讲如何使用Dify。

wal1314520的博客 1万+

java-树的遍历

在编程生活中,我们总会遇见树性结构,这几天刚好需要对树形结构操作,就记录下自己的操作方式以及过程。现在假设有一颗这样树,(是不是二叉树都没关系,原理都是一样的) 1.广度优先遍历 英文缩写为BFS即Breadth FirstSearch。其过程检验来说是对每一层节点依次访问,访问完一层进入下一层,而且每个节点只能访问一次。对于上面的例子来说,广度优先遍历的 结果是:A,B,C,D,E,F,G,H,I(假设每层节点从左到右访问)。 先往队列中插入左节点,再插右节点,这样出队就是先左节点后右节点了。 广度优先

m0_53077601的博客 3062

软件工程:数据字典

数据字典

永远怀着至诚之心拥抱世界 4273

树的4种遍历

树的四种遍历方式的总结树的四种遍历方式的总结树的四种遍历方式(前序遍历、中序遍历、后序遍历和层序遍历)是理解和操作二叉树的基础。以下是这四种遍历方式的总结:1. 前序遍历(Pre-order Traversal)访问顺序:根节点 -> 左子树 -> 右子树递归实现简单直观,先访问根节点,然后递归遍历左子树,最后递归遍历右子树。非递归实现通常使用栈来辅助遍历,先将根节点压栈,然后在循环中出栈并访问当前节点,接着将右子节点和左子节点依次压栈(注意顺序)。

ljw的博客 2556

双系统下 ubuntu 20.04 突然 开机黑屏报错 hdaudioC0D2: unable to configure disabling

今天在开实验室开双系统台式机时,ubuntu 20.04 系统下,突然在某次关机后再开机时,本来启动好好的,但是在进行图形化启动时,本来应该是显示输入账号和密码时,直接黑屏报错了,内容为 hdaudioC0D2: unable to configure disabling ,这里是记录了报错的解决全过程

qq_48717745的博客 989

NVIDIA 2025 Board Design Engineer 校招笔试

电路分析,矩形波阶跃反馈驱动​,误差信号。非常简单,数电or​离散基础。第一题是考试规矩知情确认。大二暑假做的,纯体验​。五六题太长,下篇分享。

m0_74253823的博客 1124

树的遍历(四种全)|C++

一.树的遍历 树的遍历也叫树的搜索,是指按照某种规则对树的节点进行一遍不重复的访问。按照不同的方式可以分为树的前序遍历、中序遍历、后序遍历和层序遍历。 二.前序遍历 1)树的前序遍历指的是对树按照根、左、右的规律进行访问。 遍历结果为:F, B, A, D, C, E, G, I, H 2)递归代码实现(对于前序、中序、后序遍历的递归实现非常的相似,只是改变输出数组语句的位置) //存储遍历结果的数组 vector<int> v; //前序遍历函数 vector<int

taoxing 3万+

YOLOv8改进策略【Neck】| CGA Fusion:内容引导的注意力融合模块,空间权重引导的多模态特征自适应融合

本文记录的是利用CGA Fusion 模块改进 YOLOv8 的多模态融合部分。(Content-Guided Attention Fusion)通过内容引导注意力生成空间权重,引导高低层特征的自适应融合。本文利用模块,通过内容引导注意力生成空间权重,自适应地融合两个模态的特征,在特征融合阶段实现跨模态语义对齐与噪声抑制,增强对不同模态互补特征的利用能力,从而提升模型在多模态场景下的检测鲁棒性与准确性。DEA-Net: Single image dehazing based on detail-enhanc

Limiiiing的博客 281

练手>一棵树的前序遍历为ABE###CD,#为空节点NULL,构建二叉树

1.若想返回多个值,可以创建一个结构体,返回结构体 2.递归创建二叉树 思路: 二叉树的根和size CreateTree(前序,size) { 终止条件: 1.遇到‘#’,return{NULL,1} 2.遇到size==0,return{NULL,0} 创建根,赋值; 根->left,用了多少值=CreateTree(左子树的前序,size-1); 根->right...

qq_42069216的博客 814

基于STM32F103C8T6的智能蓝牙手环系统设计与实现

摘要:本文设计并实现了一款基于STM32F103C8T6单片机的智能蓝牙手环系统。该系统集成了ADXL345加速度计、DS18B20温度传感器、plusesennor心率模块和BT06蓝牙模块,实现了心率(±2bpm)、体温(±0.5℃)、步数(±3%)等健康数据的实时监测与显示。通过OLED屏提供人机交互界面,支持蓝牙APP远程控制,具备自动步数清零、异常提醒等功能。测试表明系统各项功能均达到设计要求,响应时间0.1-0.5秒,心率检测准确率98%,体温检测准确率99%,功耗50mA(工作)/10mA(待

3254968421的博客 205

19、CATIA V5:参数化建模与零件目录创建全解析

本文详细解析了在CATIA V5中通过设计表实现参数化建模及零件目录创建的完整流程。以支撑块和钩形夹为例,介绍了参数识别、设计表创建与关联、配置管理以及零件目录的构建方法。同时,提供了实际操作建议和注意事项,帮助设计师高效创建和管理零件族,提升设计效率和质量。

raspberrypi5的博客 446

BFS遍历树和DFS遍历树

遍历树 按照遍历的顺序,绘制成树型结构 DFS遍历树 以下为图到遍历树的转化(如果不清楚图的遍历,请先阅读笔者的另一篇文章:图的遍历(动图)),按照DFS遍历的顺序,绘制成一棵树,途中红色的边就是遍历过程中没有经过的边(在遍历树上,红色的边其实是不存在的,只是为了和图做比对和便于后面的分析,笔者在遍历树上绘制出来了) 从遍历树中可以看出,非遍历的边可以指向自己的祖先节点(即后向边),而查找桥的算...

z13192905903的博客 2169

YOLOv11改进策略【模型轻量化】| 替换骨干网络为 2024轻量化网络MoblieNetV4:移动生态系统的通用模型

一、本文介绍 本文记录的是基于MobileNet V4的YOLOv11目标检测轻量化改进方法研究。MobileNet V4通过整合UIB、Mobile MQA以及优化的NAS策略,能够在在不降低性能指标的前提下,降低计算成本。本文配置了原论文中MNv4-Conv-S、MNv4-Conv-M、MNv4-Conv-L、MNv4-Hybrid-M和MNv4-Hybrid-L五种模型,以满足不同的需求。 模型 参数量 计算量 推理速度 YOLOv11m 20.0M 67.6GFLOPs 3.5ms

Limiiiing的博客 4455

树的遍历总结

本文总结一下树的几种遍历方法,分别是前序,前序非递归,中序,中序非递归,后序,后序非递归,按层次遍历,这7种。 前序遍历 先打印当前节点,再用前序遍历的方法遍历左右子树。 前序遍历非递归 遍历到当前节点,如果非空,打印这个节点,将其入栈,指向其左子节点,否则,出栈一个元素(这个元素的左子树遍历完毕),指向其右子节点。 中序遍历 用中序遍历的方法遍历左子树,打印当前节点,用中序遍历的方法打印右子数。...

yay1994的博客 1655

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

java 树的各种遍历

基础知识 树是一个有n个有限节点组成一个具有层次关系的集合,每个节点有0个或者多个子节点,没有父节点的节点称为根节点,也就是说除了根节点以外每个节点都有父节点,并且有且只有一个。 树的种类比较多,有二叉树,红黑树,AVL树,B树,哈夫曼树,字典树等等。 甚至堆我们也可以把它看成是一棵树,树的这么多种类中,我们最常见的应该是二叉树了,下面我们来看一下他的结构。 定义: 1,结点的度: 一个结点含有的子结点的个数称为该结点的度; 2,叶结点或终端结点: 度为0的结点称为叶结点; 3,非终端结点或分支结点: 度

倒骑驴走着瞧的博客 7276

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

关于树的遍历

该文章中的题目与“二叉树的遍历”思路有许多相似之处,可以对照着学习加深印象

DaisyMaria的博客 901

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

树的层次遍历

树的层次遍历,顾名思义就是在一颗树中按从左到右的顺序一层一层的遍历,这看似简单,但我们创建树时通常是根节点指向它的孩子结点,兄弟结点之间要怎么联系?这里我们可以转换一下思维,我们能不能把树每一层的结点按从左到右的顺序依次放到一个空间,然后依次输出,这样不就为每层的兄弟结点创建了联系。为了实现这样的效果,我们可以用到我们学过的一种很重要的数据结构——队列 首先我们以二叉树为例 1、先把树的根节点放入队列中 2、判断队列是否为空,若不为空执行3,4 3、如果左孩子不为空,左孩子进队列 4、如果由孩子不为空,右孩

Sveinn的博客 4461

树的四种遍历方式c++

树的常用的遍历方式有四种,分别为前序遍历,中序遍历,后续遍历和层次遍历,下面的代码依次实现了它们的递归以及非递归方法。 // tree.cpp : 此文件包含 &quot;main&quot; 函数。程序执行将在此处开始并结束。 // #include &quot;pch.h&quot; #include &amp;lt;iostream&amp;gt; #include&amp;lt;stack&amp;gt; #include&a

qq_37891889的博客 7693

树的遍历(Java)

题目描述 实现树的前序,中序,后序遍历 解决 前序和后序用的比较少,直接用递归了,中序用的比较多,用两种实现方式 树的节点 class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } 三种遍历 public class So...

LrvingTc的博客 1万+

树的前序遍历、中序遍历、后序遍历详解

树的前序遍历、中序遍历、后序遍历详解 1.前序遍历 图1 对于当前节点,先输出该节点,然后输出他的左孩子,最后输出他的右孩子。以上图为例,递归的过程如下: (1):输出 1,接着左孩子; (2):输出 2,接着左孩子; (3):输出 4,左孩子为空,再接着右孩子; (4):输出 6,左孩子为空,再接着右孩子; (5):输出 7,左右孩子都为空,此时 2 的左子树全部输出,2 的右子树为空,此时 ...

Masterheaven的博客 1451

树的遍历及图的遍历

介绍了树、二叉树、二叉搜索树,重点介绍了二叉树的广度、深度优先遍历方法以及广度优先便利的应用(层次遍历、求最短路); 介绍了图/网格的广度、深度优先遍历,重点介绍了深度优先遍历的应用:获取图中的路径

weixin_42639395的博客 1025

树的遍历及根据遍历顺序反推树的形状

三种遍历顺序 以上是网上随便找的一个树 前序遍历:FCADBEHGM(根左右) 中序遍历:ACBDFHEMG(左根右) 后序遍历:ABDCHMGEF(左右根) 中序遍历步骤 中序遍历的规则是【左根右】,我们从root节点A看起; 此时F是根节点,遍历F的左子树; F的左子树存在,找到C,此时C看做根节点,遍历C的左子树; C的左子树存在,找到A,此时A看做根节点,遍历A的左子树; A的左子树不存在,返回A,根据【左根右】的遍历规则,记录A,遍历A的右子树;(此时记录顺序为:A) A的右子树不存在,返回C,

weixin_42144277的博客 1005
上一篇: C++ 拷贝构造函数
下一篇: STL 简单知识点
_萧_
博客等级 码龄10年 55粉丝 157原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值