算法相关数据结构总结:
序号 | 数据结构 | 文章 |
---|---|---|
1 | 动态规划 | 动态规划之背包问题——01背包 动态规划之背包问题——完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 算法(Java)——动态规划 |
2 | 数组 | 算法分析之数组问题 |
3 | 链表 | 算法分析之链表问题 算法(Java)——链表 |
4 | 二叉树 | 算法分析之二叉树 算法分析之二叉树遍历 算法分析之二叉树常见问题 算法(Java)——二叉树 |
5 | 哈希表 | 算法分析之哈希表 算法(Java)——HashMap、HashSet、ArrayList |
6 | 字符串 | 算法分析之字符串 算法(Java)——字符串String |
7 | 栈和队列 | 算法分析之栈和队列 算法(Java)——栈、队列、堆 |
8 | 贪心算法 | 算法分析之贪心算法 |
9 | 回溯 | Java实现回溯算法入门(排列+组合+子集) Java实现回溯算法进阶(搜索) |
10 | 二分查找 | 算法(Java)——二分法查找 |
11 | 双指针、滑动窗口 | 算法(Java)——双指针 算法分析之滑动窗口类问题 |
二叉树的基础知识参考:算法分析之二叉树,二叉树的递归和迭代遍历方法参考:算法分析之二叉树遍历。
一、二叉树的解题方法
1. 递归法
二叉树问题中,递归是非常好用的解题方法。
每次写递归,都按照这三要素来写,可以保证大家写出正确的递归算法!
-
确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
-
确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
-
确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
遇到二叉树的算法首先可以考虑递归实现,分别递归左右子树。
2. 迭代法
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
因此,能用递归解决的问题,也可以使用栈和队列迭代实现。
使用前中后序遍历方法的话借助栈,使用层序遍历的话,使用队列。
模板请参考:算法分析之二叉树遍历。
二、leetcode例题讲解二叉树问题
1. 二叉树的属性问题
如算法分析之二叉树中描述,二叉树包括完全二叉树,满二叉树,平衡二叉树,二叉搜索树,根据这些不同属性(如深度,节点个数)的树衍生出一系列二叉树问题。
101. 对称二叉树
leetcode题目链接:101. 对称二叉树
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
解题思路:
对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
比较的是两个子树的里侧和外侧的元素是否相等。
Java代码实现:
- 递归实现
class Solution {
public boolean isSymmetric(TreeNode root) {
// 递归
if(root == null) return true;
return compare(root.left, root.right);
}
public boolean compare(TreeNode left, TreeNode right) {
if(left != null && right == null) return false;
if(left == null && right != null) return false;
if(left == null && right == null) return true;
if(left.val != right.val) return false;
boolean compare1 = compare(left.left, right.right);
boolean compare2 = compare(left.right, right.left);
return compare1 && compare2;
}
}
- 迭代实现
class Solution {
public boolean isSymmetric(TreeNode root) {
// 迭代,队列
if(root == null) return true;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root.left);
queue.offer(root.right);
while(!queue.isEmpty()) {
TreeNode node1 = queue.poll();
TreeNode node2 = queue.poll();
if(node1 == null && node2 == null) continue;
if(node1 == null || node2 == null || node1.val != node2.val) return false;
queue.offer(node1.left);
queue.offer(node2.right);
queue.offer(node1.right);
queue.offer(node2.left);
}
return true;
}
}
100. 相同的树
leetcode题目链接:100. 相同的树
给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
示例一:
输入:p = [1,2,3], q = [1,2,3]
输出:true
解题思路:
相同的树与对称二叉树的思想相似,只是从一棵树换成了两棵树。
Java实现代码:
- 递归
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
return compare(p, q);
}
private boolean compare(TreeNode p, TreeNode q) {
if(p == null && q == null) return true;
if(p == null || q == null || p.val != q.val) return false;
boolean compare1 = compare(p.left, q.left);
boolean compare2 = compare(p.right, q.right);
return compare1 && compare2;
}
}
104. 二叉树的最大深度
leetcode题目链接:104. 二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例一:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
Java代码实现:
- 递归
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
}
- 迭代
class Solution {
public int maxDepth(TreeNode root) {
// 迭代,基于层序遍历的队列实现
if(root == null) return 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int depth = 0;
while(!queue.isEmpty()) {
int size = queue.size();
depth++;
for(int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if(node.left != null) {
queue.offer(node.left);
}
if(node.right != null) {
queue.offer(node.right);
}
}
}
return depth;
}
}
111. 二叉树的最小深度
leetcode题目链接:111. 二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
示例一:
输入:root = [3,9,20,null,null,15,7]
输出:2
需要注意的是:
题目中说的是:最小深度是从根节点到最近叶子节点的最短路径上的节点数量。,注意是叶子节点。
什么是叶子节点,左右孩子都为空的节点才是叶子节点!
所以,如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。
反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。 最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。
Java代码实现:
- 递归
class Solution {
public int minDepth(TreeNode root) {
// 和最大深度一样,但需要注意的是左右子树为空的情况
if(root == null) return 0;
int left = minDepth(root.left);
int right = minDepth(root.right);
return left == 0 || right == 0 ? 1 + left + right : 1 + Math.min(left, right);
}
}
- 迭代
class Solution {
public int minDepth(TreeNode root) {
// 迭代,基于层序遍历的队列实现
if(root == null) return 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int depth = 0;
while(!queue.isEmpty()) {
int size = queue.size();
depth++;
for(int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if(node.left == null && node.right == null){
return depth;
}
if(node.left != null) {
queue.offer(node.left);
}
if(node.right != null) {
queue.offer(node.right);
}
}
}
return depth;
}
}
222. 完全二叉树的节点个数
leetcode题目链接:222. 完全二叉树的节点个数
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
示例一:
输入:root = [1,2,3,4,5,6]
输出:6
Java代码实现:
- 递归
class Solution {
public int countNodes(TreeNode root) {
if(root == null) return 0;
return countNodes(root.left) + countNodes(root.right) + 1;
}
}
- 迭代
class Solution {
public int countNodes(TreeNode root) {
// 迭代,基于层序遍历的队列实现
if (root == null) return 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int count = 0;
while (!queue.isEmpty()) {
int size = queue.size();
while (size -- > 0) {
TreeNode node = queue.poll();
count++;
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
}
return count;
}
}
110. 平衡二叉树
leetcode题目链接:110. 平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例一:
输入:root = [3,9,20,null,null,15,7]
输出:true
解题思路:
这里强调一波概念:
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。
但leetcode中强调的深度和高度很明显是按照节点来计算的,如图:
Java代码实现:
- 递归 1
class Solution {
public boolean isBalanced