1. 二叉树01
从这天开始学习了二叉树的内容
首先学习了关于二叉树的理论基础:
【代码随想录刷题】二叉树的理论基础
【代码随想录刷题】二叉树的实现java版
树
- 树是一种抽象数据类型(ADT),是一种非线性数据结构。
- 关于树的一些相关概念,如:节点的度、树的度、节点的深度以及树的高度等等。
- 树的存储形式主要有:双亲表示法、孩子表示法、孩子兄弟表示法,常用的是孩子兄弟表示法。
- 树的应用:linux 系统。
二叉树
- 是 n 个有限元素的集合
- 在我们解题过程中二叉树有两种主要的形式:满二叉树和完全二叉树。
- 二叉搜索树是一个有序树。
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树。
- 平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
- 二叉树的存储方式:可以链式存储,也可以顺序存储。我们一般是用链式来存储二叉树。
- 二叉树的遍历方式:
- 深度优先遍历
- 前序遍历(递归,迭代) 节点顺序:根左右
- 中序遍历(递归,迭代) 节点顺序:左根右
- 后序遍历(递归,迭代) 节点顺序:左右根
- 广度优先遍历
- 层次遍历(迭代)
- 深度优先遍历
然后学习了递归遍历的思路:
递归三部曲:
- 1.确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
- 2.确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
- 3.确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
最后用二叉树的前序、后序、中序遍历来练手。
(遍历可以用递归和迭代来实现,但是优先掌握递归)
2. 二叉树02
这天主要一口气打了10道层次遍历的题目:
层序遍历的思路:
- 层序遍历一个二叉树,就是从左到右一层一层的去遍历二叉树。
- 需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
- 而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。
- 对于【107】二叉树的层次遍历II,返回其节点值 自底向上的层序遍历,这道题我们只需将层次遍历得到的结果数组翻转一下即可
- 对于【199】二叉树的右视图,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值,我们每次返回每层的最后一个字段即可。
- 对于【637】二叉树的层平均值,返回每一层节点的平均值,只需层次遍历每一层之后求个和再取个平均值。
- 对于【429】N叉树的层序遍历,这道题依旧是层次遍历的模板题,只不过一个节点有多个孩子了。
- 对于【515】在每个树行中找最大值,找出该二叉树中每一层的最大值,我们可以进行层序遍历,取每一层的最大值。
- 对于【116】填充每个节点的下一个右侧节点指针,本题依然是层序遍历,只不过在单层遍历的时候记录一下本层的头部节点, 然后在遍历的时候让前一个节点指向本节点就可以了。
- 对于【117】填充每个节点的下一个右侧节点指针II,这道题目和【116】一样的,只是【116】是完美二叉树,此题是二叉树。
- 对于【104】二叉树的最大深度,这道题使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
- 对于【111】二叉树的最小深度,最小深度是从根节点到最近叶子节点的最短路径上的节点数量。和【104】相比,需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。 如果其中一个孩子为空则不是最低点。
然后还刷了【226】翻转二叉树,这道题的关键在于遍历顺序,前中后序应该选哪一种遍历顺序?
- 注意只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果
- 这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次
- 因为中序先左孩子交换孩子,再根交换孩子(做完后,右孩子已经变成了原来的左孩子), 再右孩子交换孩子(此时其实是对原来的左孩子做交换)
- 层序遍历也可以
最后刷了【101】对称二叉树,这道题目需要检查这棵二叉树是否对称。
首先我们要想清楚,判断对称二叉树要比较的是哪两个节点,要比较的可不是左右节点!
对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的。其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
我们需要比较的是两个子树的里侧和外侧的元素是否相等。如图所示:
3. 二叉树03
这天深入学习了二叉树节点的深度和二叉树节点的高度。
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
- 前序求深度,后序求高度。
- 对于【104】二叉树的最大深度,其实根节点的高度就是二叉树的最大深度,所以本题中我们可以通过后序遍历求的根节点高度来求二叉树的最大深度。
- 在前一天做这道题目时,使用的是层次遍历法,那是因为最大的深度就是二叉树的层数,两者正好相等。
- 对于【111】二叉树的最小深度,此题的重点在于读懂题意,理解最小深度 ,最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
- 对于【222】完全二叉树的节点个数,这道题使用递归法,把左右子树节点数相加再加1就可(这个1指的是根节点);使用层次遍历把每层遍历到的节点数相加即可。
4. 二叉树04
- 对于【110】平衡二叉树,首先要知道平衡二叉树的定义:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
- 既然让比较高度,那么要用后序遍历。分别求出其左右子树的高度,然后判断其差值是否小于等于1。
- 对于【257】二叉树的所有路径,要求按 任意顺序 ,返回所有从根节点到叶子节点的路径。
- 在这道题目中,首次接触到回溯。 我们要把路径记录下来,需要回溯来回退一个路径再进入另一个路径。
- 要知道递归和回溯是一家的,本题使用递归的方式,也需要回溯。
第一次在二叉树中接触到回溯,关于本题的详解可以看一下卡哥的视频,视频讲解的很清楚。
递归中带着回溯,你感受到了没?| LeetCode:257. 二叉树的所有路径
前序遍历以及回溯的过程如图:
- 对于【404】左叶子之和,首先要知道什么是左叶子
- 卡哥给出的左叶子的明确定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点
- 那么,我们如何来判断左叶子呢?判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。
- 也就是说,如果该节点的左节点不为空,该节点的左节点的左节点为空,该节点的左节点的右节点为空,则找到了一个左叶子。
5.二叉树05
-
对于【513】找树左下角的值,找出该二叉树的 最底层 最左边 节点的值。
- 本题使用迭代法很简单,只需要记录最后一行第一个节点的数值就可以了。
- 那么使用递归法呢?
- 问题1:如何判断是最后一行。其实就是深度最大的叶子节点一定是最后一行。所以要找深度最大的叶子节点。
- 问题2:如何找最左边的呢?可以使用前序遍历(当然中序,后序都可以,因为本题没有 根节点的处理逻辑,只要左优先就行),保证优先左边搜索,然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值。
- 此题也是用到了回溯。
-
对于【112】路径总和,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。
- 本题可以采用深度遍历的方式来遍历(本题前中后序都可以,无所谓,因为根节点也没有处理逻辑)。,递归法
-
- 解这道题首先要知道中序遍历与后序遍历构造二叉树的理论知识:以 后续数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后续数组。一层一层的切下去,每次后续数组的最后一个元素就是节点元素。
- 代码实现过程:
-
- 如果数组大小为0的话,说明是空节点了
-
- 如果不为空,那么取后序数组的最后一个元素作为节点元素
-
- 找到后序数组最后一个元素在中序数组中的位置,作为切割点
-
- 切割中序数组,切成中序左数组和中序右数组(顺序一定不能弄反了,一定要先切中序数组)
-
- 切割后序数组,切成后序左数组和后序右数组
-
- 递归处理左区间和右区间
-
中序遍历与后序遍历构造二叉树的流程如下图所示: