day_12二叉树理论基础以及遍历

第六章 二叉树part01

 今日内容: 

  1.  理论基础
  1.  递归遍历  
  1.  迭代遍历
  1.  统一迭代

详细布置 

 题目分类


二叉树的种类

二叉树有两种主要的形式:满二叉树和完全二叉树。

满二叉树

满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。

这棵二叉树为满二叉树,也可以说深度为k,有2^k-1个节点的二叉树。

完全二叉树

完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h1开始),则该层包含 1~ 2^(h-1) 个节点。

二叉搜索树

前面介绍的树,都没有数值的,而二叉搜索树是有数值的了,二叉搜索树是一个有序树

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉排序树

平衡二叉搜索树

平衡二叉搜索树:又被称为AVLAdelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

二叉树的存储方式

二叉树可以链式存储,也可以顺序存储。

那么链式存储方式就用指针, 顺序存储的方式就是用数组。

顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在各个地址的节点串联一起。

链式存储如图:

顺序存储的方式如图:

用数组来存储二叉树如何遍历的呢?

如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2

二叉树的遍历方式

二叉树主要有两种遍历方式:

  1. 深度优先遍历:先往深走,遇到叶子节点再往回走。
  2. 广度优先遍历:一层一层的去遍历。

深度优先遍历

前序遍历(递归法,迭代法)

中序遍历(递归法,迭代法)

后序遍历(递归法,迭代法)

广度优先遍历

层次遍历(迭代法)

这里前中后,其实指的就是中间节点的遍历顺序,只要大家记住 前中后序指的就是中间节点的位置就可以了。

二叉树的定义

public class TreeNode {

    int val;

    TreeNode left;

    TreeNode right;

    TreeNode() {}

    TreeNode(int val) { this.val = val; }

    TreeNode(int val, TreeNode left, TreeNode right) {

        this.val = val;

        this.left = left;

        this.right = right;

    }

}

递归遍历

递归算法的三个要素

  1. 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
  2. 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
  3. 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

题目1给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

/**

 * Definition for a binary tree node.

 * public class TreeNode {

 *     int val;

 *     TreeNode left;

 *     TreeNode right;

 *     TreeNode() {}

 *     TreeNode(int val) { this.val = val; }

 *     TreeNode(int val, TreeNode left, TreeNode right) {

 *         this.val = val;

 *         this.left = left;

 *         this.right = right;

 *     }

 * }

 */

class Solution {

    public List<Integer> preorderTraversal(TreeNode root) {

        List<Integer> result = new ArrayList<>();

        preorder(root,result);

        return result;

    }

    public void preorder(TreeNode root,List<Integer> result)

    {

        if(root==null) return;

        result.add(root.val);

        preorder(root.left,result);

        preorder(root.right,result);

    }

}

题目2:给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 

/**

 * Definition for a binary tree node.

 * public class TreeNode {

 *     int val;

 *     TreeNode left;

 *     TreeNode right;

 *     TreeNode() {}

 *     TreeNode(int val) { this.val = val; }

 *     TreeNode(int val, TreeNode left, TreeNode right) {

 *         this.val = val;

 *         this.left = left;

 *         this.right = right;

 *     }

 * }

 */

class Solution {

    public List<Integer> postorderTraversal(TreeNode root) {

        List<Integer> result = new ArrayList<>();

        postorder(root,result);

        return result;

    }

    public void postorder(TreeNode root,List<Integer> result)

    {

        if(root==null) return;

        postorder(root.left,result);

        postorder(root.right,result);

        result.add(root.val);

    }

}

题目3:给定一个二叉树的根节点 root ,返回 它的 中序 遍历 

/**

 * Definition for a binary tree node.

 * public class TreeNode {

 *     int val;

 *     TreeNode left;

 *     TreeNode right;

 *     TreeNode() {}

 *     TreeNode(int val) { this.val = val; }

 *     TreeNode(int val, TreeNode left, TreeNode right) {

 *         this.val = val;

 *         this.left = left;

 *         this.right = right;

 *     }

 * }

 */

class Solution {

    public List<Integer> inorderTraversal(TreeNode root) {

        List<Integer> result = new ArrayList<>();

        inorder(root,result);

        return result;

    }

    public void inorder(TreeNode root,List<Integer> result)

    {

        if(root==null) return;

        inorder(root.left,result);

        result.add(root.val);

        inorder(root.right,result);

    }

}

迭代遍历 

前序遍历(迭代法)

前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。

为什么要先加入 右孩子,再加入左孩子呢? 因为这样出栈的时候才是中左右的顺序。

迭代实现前序遍历:

/**

 * Definition for a binary tree node.

 * public class TreeNode {

 *     int val;

 *     TreeNode left;

 *     TreeNode right;

 *     TreeNode() {}

 *     TreeNode(int val) { this.val = val; }

 *     TreeNode(int val, TreeNode left, TreeNode right) {

 *         this.val = val;

 *         this.left = left;

 *         this.right = right;

 *     }

 * }

 */

class Solution {

    public List<Integer> preorderTraversal(TreeNode root) {

        List<Integer> result = new ArrayList<>();

        if(root==null) return result;

        Stack<TreeNode> stack = new Stack<>();

        stack.push(root);

        while(!stack.isEmpty())

        {

            TreeNode current = stack.pop();

            result.add(current.val);

            if(current.right!=null) stack.push(current.right);

            if(current.left!=null) stack.push(current.left);

        }

        return result;

    }

}

后序遍历(迭代法)

再来看后序遍历,先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了。

迭代实现后序遍历:

/**

 * Definition for a binary tree node.

 * public class TreeNode {

 *     int val;

 *     TreeNode left;

 *     TreeNode right;

 *     TreeNode() {}

 *     TreeNode(int val) { this.val = val; }

 *     TreeNode(int val, TreeNode left, TreeNode right) {

 *         this.val = val;

 *         this.left = left;

 *         this.right = right;

 *     }

 * }

 */

class Solution {

    public List<Integer> postorderTraversal(TreeNode root) {

        List<Integer> result = new ArrayList<>();

        if(root==null) return result;

        Stack<TreeNode> stack = new Stack<>();

        stack.push(root);

        while(!stack.isEmpty())

        {

            TreeNode current =stack.pop();

            result.add(current.val);

            if(current.left!=null) stack.push(current.left);

            if(current.right!=null) stack.push(current.right);

        }

        Collections.reverse(result);

        return result;

    }

}

中序遍历(迭代法)

在迭代的过程中,其实我们有两个操作:

  1. 处理:将元素放进result数组中
  2. 访问:遍历节点

分析一下为什么刚刚写的前序遍历的代码,不能和中序遍历通用呢,因为前序遍历的顺序是中左右,先访问的元素是中间节点,要处理的元素也是中间节点,所以刚刚才能写出相对简洁的代码,因为要访问的元素和要处理的元素顺序是一致的,都是中间节点。

那么再看看中序遍历,中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进result数组中),这就造成了处理顺序和访问顺序是不一致的。

那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。

迭代实现中序遍历:

/**

 * Definition for a binary tree node.

 * public class TreeNode {

 *     int val;

 *     TreeNode left;

 *     TreeNode right;

 *     TreeNode() {}

 *     TreeNode(int val) { this.val = val; }

 *     TreeNode(int val, TreeNode left, TreeNode right) {

 *         this.val = val;

 *         this.left = left;

 *         this.right = right;

 *     }

 * }

 */

class Solution {

    public List<Integer> inorderTraversal(TreeNode root) {

        List<Integer> result = new ArrayList<>();

        if(root==null) return result;

        Stack<TreeNode> stack = new Stack<>();

        TreeNode cur = root;

        while(cur!=null||!stack.isEmpty())

        {

            if(cur!=null)

            {

                stack.push(cur);

                cur = cur.left;

            }

            else{

                cur = stack.pop();

                result.add(cur.val);

                cur = cur.right;

            }

        }

        return result;

    }

}

统一迭代

普通的迭代法使用栈的话,无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况

那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。

如何标记呢,就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 这种方法也可以叫做标记法。

中序遍历:

/**

 * Definition for a binary tree node.

 * public class TreeNode {

 *     int val;

 *     TreeNode left;

 *     TreeNode right;

 *     TreeNode() {}

 *     TreeNode(int val) { this.val = val; }

 *     TreeNode(int val, TreeNode left, TreeNode right) {

 *         this.val = val;

 *         this.left = left;

 *         this.right = right;

 *     }

 * }

 */

class Solution {

    public List<Integer> inorderTraversal(TreeNode root) {

        List<Integer> result = new ArrayList<>();

        if(root==null) return result;

        Stack<TreeNode> stack = new Stack<>();

        stack.push(root);

        while(!stack.isEmpty())

        {

            TreeNode Node = stack.peek();

            if(Node!=null)

            {

                stack.pop();

                if(Node.right!=null) stack.push(Node.right);

                stack.push(Node);

                stack.push(null);

                if(Node.left!=null) stack.push(Node.left);

            }

            else{

                stack.pop();

                Node = stack.peek();

                stack.pop();

                result.add(Node.val);

            }

        }

        return result;

    }

}

前序遍历:

 if(Node!=null)

            {

                stack.pop();

                if(Node.right!=null) stack.push(Node.right);

                if(Node.left!=null) stack.push(Node.left);

                stack.push(Node);

                stack.push(null);

            }

后序遍历:

 if(Node!=null)

            {

                stack.pop();

                stack.push(Node);

                stack.push(null);

                if(Node.right!=null) stack.push(Node.right);

                if(Node.left!=null) stack.push(Node.left);

            }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/717582.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

进程与线程:通过实际生活来解析计算机的基本运作单位

进程与线程 进程与线程&#xff1a;详细解析计算机的基本运作单位1. 进程&#xff1a;独立的执行环境1.1 进程的特点&#xff1a; 2. 线程&#xff1a;轻量级的执行单元2.1 线程的特点&#xff1a; 3. 区别和联系4. 表格 进程与线程&#xff1a;详细解析计算机的基本运作单位 在…

Unity铰链四杆机构设计和运动仿真

一、效果图 设定好各边长度和转速后&#xff0c;点击【设置并启动】&#xff0c;自动生成一个机构模型&#xff0c;并按照原理进行运转 二、铰链四杆机构介绍 机架&#xff1a;A和D是固定位置&#xff0c;叫做机架。 曲柄&#xff1a;B点绕A点旋转&#xff0c;构成曲柄。 连…

英语学习资源分享

键盘侠的单词记忆软件&#xff1a; Qwerty Learner — 为键盘工作者设计的单词与肌肉记忆锻炼软件https://qwerty.kaiyi.cool/ 经济学人、纽约客等英语外刊杂志下载&#xff1a;若github无法进入可以试试下载VPN插件&#xff08;在浏览器中安装免费的VPN插件&#xff0c;个人推…

视频汇聚/存储/压缩/诊断平台EasyCVR视频联网整合方案应用特点

随着科技的不断发展&#xff0c;监控视频在各个领域的应用越来越广泛。为了更好地管理和利用这些视频资源&#xff0c;视频联网与整合的需求也越来越多。通过视频联网技术将不同地理位置或不同设备的视频资源进行整合&#xff0c;实现实时共享和集中管理。视频联网整合方案的应…

2024最新算法:鹦鹉优化算法(Parrot optimizer,PO)求解23个基准函数

一、鹦鹉优化算法 鹦鹉优化算法&#xff08;Parrot optimizer&#xff0c;PO&#xff09;由Junbo Lian等人于2024年提出的一种高效的元启发式算法&#xff0c;该算法从驯养的鹦鹉中观察到的觅食、停留、交流和对陌生人行为的恐惧中汲取灵感。这些行为被封装在四个不同的公式中…

C++_红黑树

目录 1、红黑树的规则 2、红黑树节点的定义 3、红黑树插入节点的调整操作 3.1 情况一 3.2 情况二 3.3 情况三 4、红黑树的实现 结语 前言&#xff1a; 在C中&#xff0c;红黑树是二叉搜索树的另一种优化版本&#xff0c;他与AVL树的区别在于保持树的平衡方式不同&…

【Mysql】Navicat数据库勿删了mysql.infoschema@localhost,导致打不开数据库,如何修改

运行报错如下&#xff1a; 1449 . The user specified as a definer (mysql.infoschemaocalhost) does not exist该方法不需要重启mysql&#xff0c;或者重装&#xff1b;仅需要恢复删除的mysql.infoschemalocalhost用户 一、登录建立用户 mysql -uroot -pxxxxxx密码二、建立…

【网上商城系统的设计与开发】

目录 1.实训概况 1 1.1 实训题目 1 1.2实训时间 1 1.3实训目的 1 1.4 实训环境 1 1.5 实训内容 2 1.6 进度安排 3 2.需求分析 5 2.1 功能需求分析 5 2.1.1用户需求分析 5 2.2.2网站前台需求 5 2.2.3网站后台需求 6 2.2 可行性分析 7 2.2.1社会可行性 7 2.2.2技术可行性 8 3.系统…

Sora学习(一):Sora技术路径整体认知

前文&#xff1a;最近跟着DataWhale组队学习这一期“Sora原理与技术实战”&#xff0c;本篇博客主要是基于DataWhale成员、厦门大学平潭研究院杨知铮研究员分享的Sora技术原理详解课件内容以及参考网上一些博客资料整理而来&#xff08;详见文末参考文献&#xff09;&#xff0…

【谈一谈】并发编程_锁的分类

【谈一谈】并发编程_锁的分类 Hello!~大家好!~每天进步一点点,日复一日,我们终将问剑顶峰 这里主要是介绍下我们常用的锁可以分为几类,目的是整体框架作用~方便后续的并发文章 说白了,这篇就是开头哈~ 本文总纲: 一.可重入锁和不可重入锁 我们开发中一般用到的都是可重入锁比如…

Photoshop 2023:重塑创意,引领数字艺术新纪元

在数字艺术的浩瀚星空中&#xff0c;Adobe Photoshop 2023&#xff08;简称PS 2023&#xff09;如同一颗璀璨的新星&#xff0c;为Mac和Windows用户带来了前所未有的创意体验。这款强大的图像处理软件不仅继承了前作的精髓&#xff0c;更在细节上进行了诸多创新&#xff0c;让每…

运行Python文件时出现‘utf-8’code can‘t decode byte 如何解决?(如图)

如图 亦或者出现“SyntaxError: Non-UTF-8 code starting with \xbb ” 出现这种问题往往是编码格式导致的&#xff0c;我们可以在py文件中的第一行加入以下代码&#xff1a; # codingutf-8或者 # codinggdk优先使用gbk编码 解释一下常用的两种编码格式&#xff1a; utf-…

朱维群将出席用碳不排碳碳中和顶层科技路线设计开发

演讲嘉宾&#xff1a;朱维群 演讲题目&#xff1a;“用碳不排碳”碳中和顶层科技路线设计开发 简介 姓名&#xff1a;朱维群 性别&#xff1a;男 出生日期&#xff1a;1961-09-09 职称&#xff1a;教授 1998年毕业于大连理工大学精细化工国家重点实验室精细化工专业&…

什么是B+树,和B树有什么不同?

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

Spring Initializer环境问题

1.基于jdk8与本地 环境准备 1)下载jdk8并安装 2&#xff09;下载maven 3.6.3并解压放入D盘maven目录下&#xff0c;去掉外层 设置阿里源 打开settings.xml,在mirrors标签之内增加&#xff0c;注意粘贴后</id>中的/有可能被删掉&#xff0c;要自己补上 <mirror>&l…

健身房预约小程序制作详细步骤解析

如果你是一位健身爱好者&#xff0c;或者是一位健身教练&#xff0c;你一定知道预约健身的痛苦。传统的预约方式不仅麻烦&#xff0c;而且效率低下。但是&#xff0c;现在&#xff0c;我们可以使用一种神仙工具——乔拓云网&#xff0c;来搭建一个属于自己的健身预约小程序&…

【VTKExamples::PolyData】第四十三期 PolyDataPointSampler

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 前言 本文分享VTK样例PolyDataPointSampler,并解析接口vtkPolyDataPointSampler,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO …

如何使用 CrewAI 构建协作型 AI Agents

一、前言 AI Agents 的开发是当前软件创新领域的热点。随着大语言模型 (LLM) 的不断进步&#xff0c;预计 AI 智能体与现有软件系统的融合将出现爆发式增长。借助 AI 智能体&#xff0c;我们可以通过一些简单的语音或手势命令&#xff0c;就能完成以往需要手动操作应用程序才能…

运维的利器–监控–zabbix–grafana

运维的利器–监控–zabbix–grafana 一、介绍 Grafana 是一个跨平台的开源的度量分析和可视化工具 , 可以通过将采集的数据查询然后可视化的展示 。zabbix可以作为数据源&#xff0c;为grafana提供数据&#xff0c;然后grafana将数据以图表或者其他形式展示出来。zabbix和gra…

基于YOLOv的目标追踪与无人机前端查看系统开发

一、背景与简介 随着无人机技术的快速发展&#xff0c;目标追踪成为无人机应用中的重要功能之一。YOLOv作为一种高效的目标检测算法&#xff0c;同样适用于目标追踪任务。通过集成YOLOv模型&#xff0c;我们可以构建一个无人机前端查看系统&#xff0c;实现实时目标追踪和可视化…