代码随想录算法训练营第13天 |二叉树的学习

目录

二叉树

理论基础

二叉树的分类

1. 满二叉树 (Full Binary Tree)

2. 完全二叉树 (Complete Binary Tree)

3. 平衡二叉树 (Balanced Binary Tree)

5. 二叉搜索树 (Binary Search Tree, BST)

二叉树的存储

1. 链式存储 (Linked Representation)

2. 顺序存储 (Sequential Representation)

遍历方式

深度优先搜索

DFS 的工作原理

基本步骤:

广度优先搜索

BFS 的工作原理

基本步骤:

二叉树的遍历

递归遍历

前序遍历

中序遍历

后序遍历

非递归遍历

前序遍历

中序遍历

后续遍历

层序遍历


二叉树

理论基础

二叉树的分类

1. 满二叉树 (Full Binary Tree)
  • 定义:满二叉树是一种二叉树,除了叶子节点外,每个节点都有两个子节点,深度为h的完美二叉树有2^h - 1个节点
  • 特点:所有非叶子节点都有两个子节点,且所有叶子节点都在同一层。
2. 完全二叉树 (Complete Binary Tree)
  • 定义:完全二叉树是一种二叉树,所有层都被完全填满,除了可能是最后一层,且最后一层的所有节点尽可能靠左。
  • 特点:没有缺失的节点,除非是在最后一层的最右边。
3. 平衡二叉树 (Balanced Binary Tree)
  • 定义:平衡二叉树是一种二叉树,其中每个节点的左子树和右子树的高度差不超过1,空树。
  • 特点:平衡二叉树保证了二叉树的高度尽可能低,从而优化了查找、插入和删除操作的效率。
5. 二叉搜索树 (Binary Search Tree, BST)
  • 定义:二叉搜索树是一种有序二叉树,其中每个节点的值大于其左子树中所有节点的值,小于其右子树中所有节点的值。
  • 特点:在理想情况下(即树是平衡的),查找、插入、删除操作的时间复杂度为O(log n)。

二叉树的存储

二叉树的存储方式有多种,常见的主要有以下两种方法:链式存储顺序存储。每种方法都有其适用场景和优缺点。

1. 链式存储 (Linked Representation)

链式存储是二叉树最常用的存储方式,特别适合用于动态变化的树结构。

特点:

  • 每个节点用一个对象或结构体来表示,包含节点的值、左子节点指针、右子节点指针,和(有时)指向父节点的指针。
  • 链式存储易于动态地添加或删除节点,不需要预先分配大量的连续内存。

节点结构

在典型的链式存储中,每个节点包含三个主要部分:

  • 值/数据域:存储节点的数据。
  • 左指针域:指向左子节点。
  • 右指针域:指向右子节点。
class TreeNode {int value;TreeNode left;TreeNode right;TreeNode(int value) {this.value = value;this.left = null;this.right = null;}
}
2. 顺序存储 (Sequential Representation)

顺序存储是将二叉树节点按某种规则顺序存储在数组中的方式,通常适用于完全二叉树或满二叉树。

特点

  • 二叉树的节点按照层次从上到下、从左到右存储在数组中。
  • 数组的第一个位置(通常从索引0或1开始)存储根节点,其他节点按照其在二叉树中的位置依次存储。

节点位置关系

假设父节点在数组中的索引为i,则:

  • 左子节点的索引2*i + 1 (或 2*i,如果数组从1开始)
  • 右子节点的索引2*i + 2 (或 2*i + 1,如果数组从1开始)
  • 父节点的索引(i-1)/2 (对于索引从0开始的情况)

遍历方式

与图论中的深度优先搜索广度优先搜索是一致的。

深度优先搜索

深度优先搜索(Depth-First Search,简称DFS)是一种遍历或搜索树或图数据结构的算法。它优先深入到树或图的一个分支的尽可能深的节点,然后再回溯并探索其他分支。这种策略使得DFS能够深入到数据结构的最深处,然后逐层返回,直到遍历所有节点。

前序遍历:中左右

中序遍历:左中右

后序遍历:左右中

DFS 的工作原理

DFS 使用栈结构来实现遍历的过程。栈可以通过显式使用栈数据结构来实现,也可以通过递归调用来隐式实现

基本步骤
  1. 访问当前节点
  2. 标记该节点为已访问,以避免重复访问。
  3. 递归地访问该节点的未被访问的相邻节点,对于树结构来说,这意味着先访问左子树,然后访问右子树。
  4. 当节点的所有相邻节点都已被访问或没有未被访问的相邻节点时,回溯到前一个节点,然后继续访问其他未被访问的节点。
  5. 重复以上步骤,直到所有节点都被访问

递归实现:

class TreeNode {int value;TreeNode left;TreeNode right;TreeNode(int value) {this.value = value;this.left = null;this.right = null;}
}public class DFS {public void depthFirstSearch(TreeNode node) {if (node == null) {return;}// 访问当前节点System.out.print(node.value + " ");// 递归地访问左子节点depthFirstSearch(node.left);// 递归地访问右子节点depthFirstSearch(node.right);}public static void main(String[] args) {TreeNode root = new TreeNode(1);root.left = new TreeNode(2);root.right = new TreeNode(3);root.left.left = new TreeNode(4);root.left.right = new TreeNode(5);DFS dfs = new DFS();dfs.depthFirstSearch(root);  // 输出: 1 2 4 5 3}
}

栈实现:

class TreeNode {int value;TreeNode left;TreeNode right;TreeNode(int value) {this.value = value;this.left = null;this.right = null;}
}public class DFS {public void depthFirstSearch(TreeNode node) {if (node == null) {return;}// 访问当前节点System.out.print(node.value + " ");// 递归地访问左子节点depthFirstSearch(node.left);// 递归地访问右子节点depthFirstSearch(node.right);}public static void main(String[] args) {TreeNode root = new TreeNode(1);root.left = new TreeNode(2);root.right = new TreeNode(3);root.left.left = new TreeNode(4);root.left.right = new TreeNode(5);DFS dfs = new DFS();dfs.depthFirstSearch(root);  // 输出: 1 2 4 5 3}
}

广度优先搜索

广度优先搜索(Breadth-First Search,简称 BFS)是一种遍历或搜索树或图数据结构的算法。与深度优先搜索(DFS)不同,BFS 从起始节点开始,首先访问其所有邻居节点,然后再逐层向下深入到下一级的节点。这种逐层访问的方式确保了在无权重图中,从起始节点到任意其他节点的最短路径可以通过 BFS 得到。

BFS 的工作原理

BFS 使用队列(Queue)数据结构来实现逐层遍历的过程。队列遵循先进先出(FIFO)的原则,确保先进入队列的节点先被处理。

基本步骤
  1. 将起始节点加入队列,并标记为已访问。
  2. 从队列中取出一个节点,访问该节点,并将其所有未被访问的相邻节点加入队列。
  3. 标记这些相邻节点为已访问,以防止重复访问。
  4. 重复步骤 2 和 3,直到队列为空,表示所有节点都已被访问。

使用队列实现:

import java.util.LinkedList;
import java.util.Queue;class TreeNode {int value;TreeNode left;TreeNode right;TreeNode(int value) {this.value = value;this.left = null;this.right = null;}
}public class BFS {public void breadthFirstSearch(TreeNode root) {if (root == null) {return;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {TreeNode node = queue.poll();System.out.print(node.value + " ");// 将左子节点加入队列if (node.left != null) {queue.offer(node.left);}// 将右子节点加入队列if (node.right != null) {queue.offer(node.right);}}}public static void main(String[] args) {TreeNode root = new TreeNode(1);root.left = new TreeNode(2);root.right = new TreeNode(3);root.left.left = new TreeNode(4);root.left.right = new TreeNode(5);BFS bfs = new BFS();bfs.breadthFirstSearch(root);  // 输出: 1 2 3 4 5}
}

二叉树的遍历

递归遍历

1.确定递归函数的参数和返回值

2.确定终止条件

3.确定单层递归的逻辑

前序遍历
 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);}
中序遍历
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);}
后序遍历
 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);}

非递归遍历

null在谁前就先访问谁,null后如果还要push就先访问push进去的,再访问null

前序遍历

前序遍历,访问的元素和要处理的元素一致

每次循环操纵栈中第一个节点

 public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result = new LinkedList<>();Stack<TreeNode> st = new Stack<>();//放入第一个元素,放进栈中if (root != null) {st.push(root);}while (!st.empty()) {//储存栈中的第一个节点TreeNode node = st.peek();if (node != null) {//因为在后面还要压入一次中节点st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)st.push(node);                          // 添加中节点st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。} else { // 只有遇到空节点的时候,才将下一个节点放进结果集st.pop();           // 将空节点弹出node = st.peek();    // 重新取出栈中元素st.pop();result.add(node.val); // 加入到结果集}}return result;}

中序遍历

先看节点的左孩子为空则弹出中间,右孩子也为空则继续弹出前一个节点

左孩子不为空,则继续用栈收集

左孩子为空,弹出中间,右孩子不为空,收集右孩子,再看右孩子的左孩子是否为空

 public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new LinkedList<>();Stack<TreeNode> st = new Stack<>();if (root != null) st.push(root);while (!st.empty()) {TreeNode node = st.peek();if (node != null) {st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)st.push(node);                          // 添加中节点st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)} else { // 只有遇到空节点的时候,才将下一个节点放进结果集st.pop();           // 将空节点弹出node = st.peek();    // 重新取出栈中元素st.pop();result.add(node.val); // 加入到结果集}}return result;}
后续遍历

将左右交换,然后再将数组颠倒

  public List<Integer> postorderTraversal(TreeNode root) {List<Integer> result = new LinkedList<>();Stack<TreeNode> st = new Stack<>();if (root != null) st.push(root);while (!st.empty()) {TreeNode node = st.peek();if (node != null) {st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中st.push(node);                          // 添加中节点st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)         } else { // 只有遇到空节点的时候,才将下一个节点放进结果集st.pop();           // 将空节点弹出node = st.peek();    // 重新取出栈中元素st.pop();result.add(node.val); // 加入到结果集}}return result;}

层序遍历

每次循环,先得到一个队列的大小为size,然后得到左右孩子,每次弹出一个,直到弹出size个,

下次循环再得到size

// 102.二叉树的层序遍历
class Solution {public List<List<Integer>> resList = new ArrayList<List<Integer>>();public List<List<Integer>> levelOrder(TreeNode root) {//checkFun01(root,0);checkFun02(root);return resList;}//BFS--递归方式public void checkFun01(TreeNode node, Integer deep) {if (node == null) return;deep++;if (resList.size() < deep) {//当层级增加时,list的Item也增加,利用list的索引值进行层级界定List<Integer> item = new ArrayList<Integer>();resList.add(item);}resList.get(deep - 1).add(node.val);checkFun01(node.left, deep);checkFun01(node.right, deep);}//BFS--迭代方式--借助队列public void checkFun02(TreeNode node) {if (node == null) return;Queue<TreeNode> que = new LinkedList<TreeNode>();que.offer(node);while (!que.isEmpty()) {List<Integer> itemList = new ArrayList<Integer>();int len = que.size();while (len > 0) {TreeNode tmpNode = que.poll();itemList.add(tmpNode.val);if (tmpNode.left != null) que.offer(tmpNode.left);if (tmpNode.right != null) que.offer(tmpNode.right);len--;}resList.add(itemList);}}
}

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

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

相关文章

Golang | Leetcode Golang题解之第363题矩形区域不超过K的最大数值和

题目&#xff1a; 题解&#xff1a; import "math/rand"type node struct {ch [2]*nodepriority intval int }func (o *node) cmp(b int) int {switch {case b < o.val:return 0case b > o.val:return 1default:return -1} }func (o *node) rotate…

pyro 教程 时间序列 单变量,重尾,python pytorch,教程和实例 Forecasting预测,布朗运动项、偏差项和协变量项

预测I:单变量&#xff0c;重尾 本教程介绍了预测模块&#xff0c;用Pyro模型进行预测的框架。本教程只涵盖单变量模型和简单的可能性。本教程假设读者已经熟悉慢病毒感染和张量形状. 另请参见: 预测II:状态空间模型 预测三:层次模型 摘要 要创建预测模型: 创建预测模型班级…

算法笔试-编程练习-H-02-24

w这套题&#xff0c;侧重模拟和题目理解&#xff0c;只要按照题目描述正常复现整体分数应该不错 一、数据重删 数据重删是一种节约存储空间的技术&#xff0c;通常情况下&#xff0c;在数据存储池内是有很多重复的数据库。重删则是将这些重复的数据块找出并处理的技术。简单地…

Java:jdk8之后新增的时间API

文章目录 为什么要使用新增的API新增了哪些&#xff1f;Local常用方法代码一样的用法 黑马学习笔记 使用新增的 为什么要使用新增的API 新增了哪些&#xff1f; Local 常用方法 代码 package NewTime;import java.time.LocalDate;/*** Author: ggdpzhk* CreateTime: 2024-08-…

竞猜足球核心算法源码

需要实现的功能如下&#xff1a; 仅用于学习 竞猜足球核心算法源码 package com.lotterysource.portsadmin.service; import com.aliyun.oss.common.utils.DateUtil; import com.fasterxml.jackson.core.type.TypeReference; import com.lotterysource.portsadmin.dbprovid…

@ohos.systemParameterEnhance系统参数接口调用:控制设备硬件(执行shell命令方式)

本文介绍如何使用应用ohos.systemParameterEnhance (系统参数)(系统接口)来控制设备硬件&#xff0c;可以通过它在系统中执行一段shell命令&#xff0c;从而实现控制设备的效果。接下来以一个实际的样例来演示如何通过它来控制设备以太网接口 开源地址&#xff1a;https://git…

链表OJ题——环形链表2

文章目录 一、题目链接二、解题思路三、解题代码 一、题目链接 环形链表2 题目描述&#xff1a;在链表有环的基础上&#xff0c;找出环的入口点。 二、解题思路 三、解题代码

超实用的8个无版权、免费、高清图片素材网站整理

不管是设计、文章配图&#xff0c;还是视频制作&#xff0c;图片都至关重要。但是图片版权一直都是困扰很多设计、自媒体以及企业的大问题。现在&#xff0c;因为图片侵权被告的案例已经是司空见惯了&#xff0c;有的公众号甚至因为图片版权问题遭受致命打击。 1. Pexels Pexe…

(经验)SVN降版本,保留版本信息和用户信息。

背景&#xff1a;由于开始公司人数规模小&#xff0c;没有关心SVN最新版本免费对于用户数量限制要求不敏感&#xff0c;随着人数越来越多&#xff0c;公司来了新员工已经添加不了SVN需要注册码了&#xff0c;不利于SVN文件管理的在公司内部的推广。看了好多资料&#xff0c;都没…

信息学奥赛初赛天天练-75-NOIP2016普及组-完善程序-二分答案、二分查找、贪心算法、贪心策略

文章PDF链接: https://pan.baidu.com/s/1SVcGU_rApvoUWrUoviPCiA?pwdht2j 提取码: ht2j 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 1 完善程序 (单选题 &#xff0c;每小题3分&#xff0c;共30分) 郊游活动 有 n名同学参加学校组织的郊游活动&#xff0c…

有没有比较好用的在线翻译工具?实力推荐这4款。

当我们面对外文资料时&#xff0c;可能需要翻阅厚重的词典&#xff0c;耗费大量的时间和精力。在翻译这方面&#xff0c;很多人都十分依赖翻译工具的&#xff0c;因为这些工具只需几秒钟就能给出翻译结果&#xff0c;提高了我们的学习和工作的效率。但是随着翻译工具越来阅读&a…

前后端分离项目实战-通用管理系统搭建(前端Vue3+ElementPlus,后端Springboot+Mysql+Redis)第八篇:Tab标签页的实现

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

使用C++封装顺序表

作业&#xff1a;使用C手动封装一个顺序表&#xff0c;包含成员数组一个&#xff0c;成员变量N个 #include <iostream>using namespace std;using datatypeint; #define MAX 20struct SeqList { private: //私有datatype *data;int size0; …

【Java】数据类型与变量(二)

目录 3.变量 3.1什么是变量&#xff08;变量的概念&#xff09; 3.2语法格式 ​编辑​编辑3.3整型变量 3.3.1整型变量如何定义 ​编辑 3.3.2长整型变量 3.3.3短整型变量 3.3.4字节型变量 3.4浮点型变量 3.4.1双精度浮点型 3.4.2单精度浮点型 3.4.3单精度浮点型与双…

Google Search Console:完整教程

Google 提供了各种工具来收集和分析网站数据&#xff0c;其中最有价值的工具之一是 Google Search Console &#xff08;GSC&#xff09;。前身为 Google Webmaster Tools&#xff0c;它为 SEO 提供了对网站性能的宝贵见解。自 2015 年推出以来&#xff0c;该平台取得了长足的发…

关机软件项目规划

一、概述 1.1 编写目的 此项目开发规划书的编写主要是为《UPS SNMP卡网络监控系统》中配套使用的关机软件做主要的规划和整合&#xff0c;在开发过程中起到引导作用&#xff0c;以及给使用者提供简要的说明。 1.2 项目背景 关机软件是UPS网络监控适配器项目监控层的组成部分…

黑神化爆火,悟空的八十一难究竟用到了什么数据库?

九九八十一难&#xff0c;第一难。猿神&#xff0c;启动…然后发现先解压缩&#xff0c;后着色编译。就这姿势&#xff0c;这就是爆火的 《黑神话&#xff1a;悟空》单机游戏&#xff0c;哪怕是在工作日&#xff0c;大家仍纷纷涌入这个游戏世界。8月20日&#xff0c;万众瞩目的…

Excel表格合并后同步修改行号,删除重复项,按合并后的列进行排序

Excel合并单元格后每个合并后的行占据多列&#xff0c;如何进行排序 1、全选后选择合并选项中的取消合并单元格 2、选择删除重复项&#xff08;可以直接选定唯一行&#xff09; 3、可以发现合并后的每行占Excel的一行 4、然后制定排序规则 5、序号列下拉重排&#xff08;鼠标放…

智谱开源 CogVideoX-5B 视频生成模型,RTX 3060 显卡可运行;曝 OpenAI 模型「草莓」今秋推出

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

Android Studio Koala下载并安装,测试helloworld.

1、下载&#xff1a; 下载 Android Studio 和应用工具 - Android 开发者 | Android Developers 2、滚动条拉到近最后&#xff0c;各个系统的下载地址&#xff1a; 3、下载完成以后&#xff0c;我们双击运行安装&#xff1a; 如果有路径要修改&#xff0c;则修改下就可以了&a…