数据结构与算法【树】

news/2024/10/21 6:45:52/

二叉树性质

满二叉树在这里插入图片描述

深度为k,有2k−12^{k}-12k1个结点的二叉树,为满二叉树。

完全二叉树

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

二叉树的存储方式

包括链式存储和顺序存储
由于链式存储的二叉树更有利于我们理解,所以我们一般都是用链式存储二叉树。
所以大家要了解,用数组依然可以表示二叉树。

在这里插入图片描述

二叉树链式存储代码

struct TreeNode{int val;TreeNode *left;TreeNode *right;TreeNode(int x):val(x),left(NULL),right(NULL){}
};

二叉树的遍历方式

遍历方式分两类,四种

关于二叉树的遍历方式,首先从深度和广度来区分。

  1. 深度优先遍历:先往深走,遇到叶子节点再往回走。
  2. 广度优先遍历:一层一层地去遍历。
    这两种遍历是图论中最基本的两种遍历方式,后面在介绍图论的时候,还会介绍到。

那么我们进一步扩展深度优先遍历和广度优先遍历,才会有更为细致的遍历方式的区分:

  • 深度优先遍历
    • 前序遍历(递归法,迭代法)
    • 中序遍历(递归法,迭代法)
    • 后序遍历(递归法,迭代法)
  • 广度优先遍历
    • 层次遍历(迭代法)
      在深度优先遍历中:有三个顺序,前中后序遍历,有同学总分不清这三个顺序,经常搞混,我这里教大家一个技巧。
      这里前中后,其实指的就是中间节点的遍历顺序,只要大家记住,前中后序指的就是中间节点的位置就可以了。看如下节点的遍历顺序,就可以发现中间节点的顺序就是所谓的遍历方式名称的由来:
  • 前(先)序遍历:中左右
  • 中序遍历:左中右
  • 后序遍历:左右中

遍历方式的实现

最后再说一说二叉树中深度优先遍历和广度优先遍历的实现方式。我们做二叉树相关的题目,经常会使用递归的方式来实现深度优先遍历。
之前讲栈的时候,说过栈其实就是递归的一种实现结构,先进后出。也就就是说前中后序遍历的逻辑其实都是可以借助栈使用非递归的方式来实现。(通过栈的结构避免了递归操作)

而广度优先遍历的实现,一般借助队列来实现,这也是由于队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。

这里其实我们又了解了栈与队列的一个应用场景了。
具体的实现我们后面都会讲的,这里大家先要清楚这些理论基础。

二叉树与递归(二叉树的递归遍历)

说到二叉树,就不得不说递归,很多同学对递归都是又熟悉又陌生,递归的代码一般很简短,但每次都是一看就会,一写就废。

递归写不好的根本原因就是不成体系,没有递归方法论。通过二叉树的前中后序的递归写法,我们把递归方法论确定下来,进而应对复杂的递归题目。

首先,每次写递归算法,先确定三要素:

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

那三要素怎么切入,怎么找呢?
我们以前序遍历为例子,来找感觉!
1.确定递归函数的参数和返回值:因为我们要打印前序遍历节点的数值,因此参数里需要传入vector来放节点的数值,除了这一点就不需要再处理什么数据了,也不需要有返回值,所以递归函数返回类型就是void,代码如下:
(代码随想录点评:为什么要传入vector没讲清楚,另外代码还传入了节点本身,也很令人困惑?因为后面说的是除了vector不需要再处理什么数据了,也不需要有返回值,但是没提到传入TreeNode *cur的目的,但是我的猜想是,对树本身进行处理,肯定是要传入当前处理的树节点的指针的,至于为什么一定要用vector来放节点的数值,我想不通,希望后面会想通)

void traversal(TreeNode* cur, vector<int>& vec)

2.确定终止条件:在递归过程中,如何算递归结束?对于前序遍历,如果当前遍历的节点是空了,就说明递归结束了,所以如果当前遍历的节点是空,就直接return,代码如下:

if (cur == NULL) return;

3.确定单层递归的逻辑:前序遍历是中左右的顺序,因此单层递归的逻辑就是,先取中点节点的数值,(单层递归的逻辑这个概念讲的也不通!! 首先,什么是单层递归?如果重复调用单层递归实现递归的过程? 我的理解就是,每读到一个新数据,应当怎么处理这个数据,这个就是单层递归的数据处理逻辑!!!数据处理完成之后,就到了递归的逻辑了,单层递归本质上在数据处理完之后就结束了!!!后面就是递归的逻辑,例如这里递归下一个处理的数据是左子树的节点,因此对左子树进行递归,然后处理右子树,这里就继续对右子树进行递归!! 因此这里所说的单层递归,本质上就是一次数据处理过程+后面需要继续递归的数据!!!)

因此,代码如下

vec.push_back(cur->val);    // 中
traversal(cur->left, vec);  // 左
traversal(cur->right, vec); // 右

到这,我仍然看不懂,原因在于,我不知道TreeNode* cur的意义,以及vector &vec的意义,这就是代码随想录这一部分的败笔,读者很难理解!!!

然而,当读者继续往下读,读到整体代码的时候,就会恍然大悟:

class Solution {
public:void traversal(TreeNode* cur, vector<int>& vec) {if (cur == NULL) return;vec.push_back(cur->val);    // 中traversal(cur->left, vec);  // 左traversal(cur->right, vec); // 右}vector<int> preorderTraversal(TreeNode* root) {vector<int> result; //vector就是我们的遍历结果存储向量traversal(root, result);//遍历需要输入树的根节点return result;}
};

vector就是我们的遍历结果存储向量;遍历需要根据树逐步往下走,因此需要传入树根,并根据树根往下走,因此需要传入TreeNode* cur这参数。


http://www.ppmy.cn/news/10280.html

相关文章

逻辑漏洞渗透与攻防(六)之其他类型逻辑漏洞

目录 其他类型逻辑漏洞 数据包重放漏洞 条件竞争漏洞 订单金额任意修改 接口无限制枚举 支付漏洞 修改商品数量 修改支付状态 修改附属值 越权支付 无限制试用 支付漏洞总结 SRC中的逻辑漏洞总结 其他类型逻辑漏洞 数据包重放漏洞 漏洞介绍&#xff1a;通…

揣着一口袋的阳光满载而归--爱摸鱼的美工(13)

-----------作者&#xff1a;天涯小Y 揣着一口袋的阳光满载而归&#xff01; 慷懒周末 睡到自然醒&#xff0c;阳光洒在书桌上 套进宽松自在的衣服里 出门&#xff0c;去楼下坐坐 在阳光里吃午餐 在阳光里打个盹 在阳光里看猫咪上蹿下跳 在阳光里点个咖啡外卖 虚度时光&#xf…

【Acwing 周赛#85】AcWing 4792. 最大价值 + AcWing 4793. 危险程度

目录 4791. 死或生 - 简单模拟ac 4792. 最大价值 - 简单构造ac 4793. 危险程度 - dfs / 并查集 1、dfs 2、并查集 4791. 死或生 - 简单模拟ac import java.util.*;class Main {public static void main(String[] args){Scanner scnew Scanner(System.in);int nsc.nextInt(…

【系列01】java运算符及运算符优先级[附带目录 按需服用]

运算符、三元运算符、位运算符、拓展赋值、运算优先级、自增自减 运算符 java代码优先级多用括号**,多用括号()**不仅方便而且增加可读性 自增自减 a 是先赋值再增加a 是先增加再赋值上面都表示 a a1;自减同理由 public class Demo05 {public static void main(String[] ar…

【Linux】文本编辑器-vim使用

目  录1 vim的基本概念2 vim的基本操作3 vim常用模式命令集3.1 vim正常模式命令集3.2 vim末行模式命令集4 vim的简单配置1 vim的基本概念 vim编辑器与vi编辑器一样都是多模式编辑器&#xff0c;不同的是vim编辑器是vi编辑器的升级版本&#xff0c;vim不仅兼容vi的所有指令&am…

1 机器学习之线性回归

学习笔记自&#xff0c;慕课网 《Python3 入门人工智能》 https://coding.imooc.com/lesson/418.html#mid33109 麻雀虽小&#xff0c;五脏俱全 1.1 回归分析 1.2 线性回归问题求解 1.3 寻找最合适的 a、b&#xff0c;引入损失函数的概念 尽可能使损失函数最小即找到了最合适的…

Java中indexOf函数详解

1.定义 Java String 类的 indexOf() 方法返回指定字符串中指定字符或字符串第一次出现的位置。 String 类的 indexOf() 方法在字符串中查找子字符串出现的位置&#xff0c;如果存在返回字符串出现的位置&#xff08;第一位为0&#xff09;&#xff0c;如果不存在返回 -1&#x…

Java 如何不使用 volatile 和锁实现共享变量的同步操作

前言 熟悉 Java 并发编程的都知道&#xff0c;JMM(Java 内存模型) 中的 happen-before(简称 hb)规则&#xff0c;该规则定义了 Java 多线程操作的有序性和可见性&#xff0c;防止了编译器重排序对程序结果的影响。 按照官方的说法&#xff1a; 当一个变量被多个线程读取并且至…