递归
leetcode-104-二叉树的最大深度
力扣-104
题目:给定一个二叉树,找出其最大深度。
示例:
给定二叉树 [3,9,20,null,null,15,7],返回它的最大深度 3 。
3
/ \
9 20
/ \
15 7
思路:遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
}
}
时间复杂度:每个结点只访问一次,因此时间复杂度为 O(N),其中 N 是结点的数量。
空间复杂度:在最糟糕的情况下,树是完全不平衡的,例如每个结点只剩下左子结点,递归将会被调用 N 次(树的高度),因此保持调用栈的存储将是 O(N)。但在最好的情况下(树是完全平衡的),树的高度将是log(N)。因此,在这种情况下的空间复杂度将是 O(log(N))。
leetcode-110-平衡二叉树
力扣-110
题目:给定一个二叉树,判断它是否是高度平衡的二叉树。一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例:
给定二叉树 [3,9,20,null,null,15,7
返回true
3
/ \
9 20
/ \
15 7
思路:遍历,参考平衡二叉树(从底至顶,从顶至底).
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
//自底向上
class Solution {
public boolean isBalanced(TreeNode root) {
return MaxDepth(root)!=-1;
}
public int MaxDepth(TreeNode root){
if(root == null) return 0;
int left = MaxDepth(root.left);
if(left == -1) return -1;
int right = MaxDepth(root.right);
if(right == -1) return -1;
return Math.abs(left - right) < 2 ? Math.max(left, right) + 1 : -1;
}
}
时间复杂度:O(N),N 为树的节点数;最差情况下,需要递归遍历树的所有节点。
空间复杂度 :O(N) 最差情况下(树退化为链表时),系统递归需要使用 O(N)的栈空间。
leetcode-543-二叉树的直径
力扣-543
题目:给定一个二叉树,找出直径。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
示例:
给定二叉树
1
/ \
2 3
/ \
4 5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
思路:遍历,以每一个节点为中心点计算最长路径(左子树边长+右子树边长)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int max = 0;
public int diameterOfBinaryTree(TreeNode root) {
if(root == null){
return 0;
}
dfs(root);
return max;
}
public int dfs(TreeNode root){
if(root.left == null&& root.right ==null){
return 0;
}
int leftSize = root.left == null? 0:dfs(root.left)+1;
int rightSize = root.right == null? 0:dfs(root.right)+1;
max = Math.max(max,leftSize+rightSize);
return Math.max(leftSize,rightSize);
}
}
时间复杂度:O(N),其中N 为二叉树的节点数,即遍历一棵二叉树的时间复杂度,每个结点只被访问一次。
空间复杂度:O(Height),其中 Height 为二叉树的高度。由于递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度,而递归的深度显然为二叉树的高度,并且每次递归调用的函数里又只用了常数个变量,所以所需空间复杂度为 O(Height)。
类似解法的树算法题:
124. 二叉树中的最大路径和
687. 最长同值路径
leetcode-226-翻转二叉树
力扣-226
题目:翻转一棵二叉树。
示例:
输入:
4
/ \
2 7
/ \ / \
1 3 6 9
输出:
4
/ \
7 2
/ \ / \
9 6 3 1
思路:遍历,前序、中序、后序遍历
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null){
return null;
}
//前序遍历
TreeNode rightTree = root.right;
root.right = invertTree(root.left);
root.left = invertTree(rightTree);
return root;
}
}
时间复杂度: 树中的每个节点都只被访问一次,那么时间复杂度就是 O(n)
空间复杂度: 使用了递归,在最坏情况下栈内需要存放 O(h)其中 h 是树的高度,空间复杂度为 O(n)。
leetcode-617-合并二叉树
力扣-617
题目:合并二叉树。
示例:
输入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/ \
4 5
/ \ \
5 4 7
思路:遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if(t1 == null){
return t2;
}
if(t2 == null){
return t1;
}
t1.val+=t2.val;
t1.left = mergeTrees(t1.left,t2.left);
t1.right = mergeTrees(t1.right,t2.right);
return t1;
}
}
时间复杂度: O(N),其中 N是两棵树中节点个数的较小值。
空间复杂度: O(N),在最坏情况下,会递归 N层,需要 O(N) 的栈空间。
leetcode-112-路径总和
力扣-112
题目:给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
思路:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
if(root == null){
return false;
}
if(root.left==null&&root.right==null){
return sum==root.val;
}
return hasPathSum(root.left,sum-root.val)||hasPathSum(root.right,sum-root.val);
}
}
时间复杂度: O(N),其中 N 是树的节点数。对每个节点访问一次。
空间复杂度: O(H),其中 H 是树的高度。空间复杂度主要取决于递归时栈空间的开销,最坏情况下,树呈现链状,空间复杂度为 O(N)。平均情况下树的高度与节点数的对数正相关,空间复杂度为 O(logN)。
leetcode-437-统计路径和等于一个数的路径数量
力扣-437
题目:
给定一个二叉树,它的每个结点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
示例:
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
5 -3
/ \ \
3 2 11
/ \ \
3 -2 1
返回 3。和等于 8 的路径有:
1. 5 -> 3
2. 5 -> 2 -> 1
3. -3 -> 11
返回 3。和等于 8 的路径有:
- 5 -> 3
- 5 -> 2 -> 1
- -3 -> 11
思路:双重递归,外面的递归转换root point,里面的递归是在root point下找到路径的方法。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int count;
public int pathSum(TreeNode root, int sum) {
if(root == null){
return 0;
}
dfs(root, sum);
pathSum(root.left,sum);
pathSum(root.right,sum);
return count;
}
public void dfs(TreeNode root, int sum){
if(root == null){
return ;
}
sum-=root.val;
if(sum==0) count+=1;
dfs(root.left,sum);
dfs(root.right,sum);
}
}
leetcode-572-子树
力扣-572
题目:给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。
示例:
示例 1:
给定的树 s:
3
/ \
4 5
/ \
1 2
给定的树 t:
4
/ \
1 2
返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。
示例 2:
给定的树 s:
3
/ \
4 5
/ \
1 2
/
0
给定的树 t:
4
/ \
1 2
返回 false。
思路:递归,一个树要是另一个树的子树,要么两个数相同,要么是那个树的右子树,或者左子树。空树是任何树的子树
/**
* 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 boolean isSubtree(TreeNode s, TreeNode t) {
if(s==null) return false;
if(t==null) return true;
return isSameTree(s,t)||isSubtree(s.left,t)||isSubtree(s.right,t);
}
public boolean isSameTree(TreeNode s,TreeNode t){
if(s == null&&t == null){
return true;
}
if(s == null||t == null){
return false;
}
if(s.val!=t.val){
return false;
}
return isSameTree(s.left,t.left)&&isSameTree(s.right,t.right);
}
}
leetcode-101-对称二叉树
力扣-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
思路:递归 || 迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
//递归
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null) return true;
return isSame(root.left,root.right);
}
public boolean isSame(TreeNode root1, TreeNode root2 ){
if(root1==null &&root2==null){
return true;
}
if(root1 ==null || root2==null||root1.val!=root2.val){
return false;
}
return isSame(root1.left,root2.right)&&isSame(root1.right,root2.left);
}
}
时间复杂度: 这里遍历了这棵树,渐进时间复杂度为 O(n)。
空间复杂度: 这里的空间复杂度和递归使用的栈空间有关,这里递归层数不超过 n,故渐进空间复杂度为 O(n)。
leetcode-111-二叉树的最小深度
力扣-111
题目:给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最小深度 2.
思路:递归 ,注意当左右子树有一个为空时,最小深度为非空左右子树的深度+1,其他的和求最大深度相反即可
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int minDepth(TreeNode root) {
if(root==null){
return 0;
}
//null结点不参与比较
if(root.left ==null&&root.right!=null){
return 1+minDepth(root.right);
}
if(root.right==null&&root.left!=null){
return 1+minDepth(root.left);
}
return Math.min(minDepth(root.left),minDepth(root.right))+1;
}
}
时间复杂度:O(N),其中 N 是结点的数量。
空间复杂度:在最糟糕的情况下,树是完全不平衡的,例如每个结点只剩下左子结点,递归将会被调用 N 次(树的高度),因此保持调用栈的存储将是 O(N)。但在最好的情况下(树是完全平衡的),树的高度将是log(N)。因此,在这种情况下的空间复杂度将是 O(log(N))。
leetcode-404-左叶子之和
力扣-404
题目:计算给定二叉树的所有左叶子之和。
示例:
3
/ \
9 20
/ \
15 7
在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24
思路:递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int sum;
public int sumOfLeftLeaves(TreeNode root) {
if(root==null) return 0;
//判断是否是左子叶
if(root.left!=null&&(root.left.left==null&&root.left.right==null)){
sum +=root.left.val;
}
sumOfLeftLeaves(root.left);
sumOfLeftLeaves(root.right);
return sum;
}
}
时间复杂度:O(N),其中 N 是结点的数量。
空间复杂度:在最糟糕的情况下,树是完全不平衡的,例如每个结点只剩下左子结点,递归将会被调用 N 次(树的高度),因此保持调用栈的存储将是 O(N)。但在最好的情况下(树是完全平衡的),树的高度将是log(N)。因此,在这种情况下的空间复杂度将是 O(log(N))。
leetcode-687-最长同值路径
力扣-687
题目:给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。
注意:两个节点之间的路径长度由它们之间的边数表示。
示例:
输入:
5
/ \
4 5
/ \ \
1 1 5
输出: 2
思路:递归 ,判断左右子树和根节点的值是否相同。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int max = 0;
public int longestUnivaluePath(TreeNode root) {
if(root == null){
return 0;
}
dfs(root);
return max;
}
public int dfs(TreeNode root){
if(root.left==null&&root.right==null){
return 0;
}
int leftsize = root.left==null?0:dfs(root.left)+1;
int rightsize = root.right==null?0:dfs(root.right)+1;
if(leftsize>0&&root.left.val!=root.val){
leftsize = 0;
}
if(rightsize>0&&root.right.val!=root.val){
rightsize = 0;
}
max = Math.max(max,leftsize+rightsize);
return Math.max(leftsize,rightsize);
}
}
时间复杂度:O(N),其中N 为二叉树的节点数,即遍历一棵二叉树的时间复杂度,每个结点只被访问一次。
空间复杂度:O(Height),其中 Height 为二叉树的高度。由于递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度,而递归的深度显然为二叉树的高度,并且每次递归调用的函数里又只用了常数个变量,所以所需空间复杂度为 O(Height)。
leetcode-337-间隔遍历
力扣-337
题目:间隔遍历,取得最大值
示例:
3
/ \
2 3
\ \
3 1
输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
思路:递归
能盗取的最高金额为: 抢劫该节点+抢劫该节点的左孩子的左右子树+抢劫该节点的右孩子的左右子树与抢劫该节点的左子树+抢劫该节点的右子树的和 的最大值
class Solution {
public int rob(TreeNode root) {
if(root==null) return 0;
int sum=0;
if(root.left!=null) sum+=rob(root.left.left)+rob(root.left.right);
if(root.right!=null)sum+=rob(root.right.left)+rob(root.right.right);
return Math.max(rob(root.left)+rob(root.right),sum+root.val);
}
}
leetcode-671-二叉树中第二小结点
力扣-337
题目:给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。
给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1 。
示例:
输入:
2
/ \
2 5
/ \
5 7
输出: 5
说明: 最小的值是 2 ,第二小的值是 5 。
思路:递归,相当于转换成求,左右子节点中的最小值
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int findSecondMinimumValue(TreeNode root) {
return traversal(root,root.val);
}
public int traversal(TreeNode root ,int value){
if(root == null) return -1;
if(root.val>value) return root.val;
//寻找左右子节点,第一个大于自己的
int leftsum = traversal(root.left,value);
int rightsum = traversal(root.right,value);
//如果两棵子树均存在大于最小值的节点,那么返回较小的那一个
if(leftsum>=0&&rightsum>=0){
return Math.min(leftsum,rightsum);
}
//否则,其余情况均返回较大的那一个
else{return Math.max(leftsum,rightsum);}
}
}
层次遍历
leetcode-637-二叉树的层平均值
力扣-337
题目:给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
示例:
输入:
3
/ \
9 20
/ \
15 7
输出:[3, 14.5, 11]
解释:
第 0 层的平均值是 3 , 第1层是 14.5 , 第2层是 11 。因此返回 [3, 14.5, 11] 。
思路:层次遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int findSecondMinimumValue(TreeNode root) {
return traversal(root,root.val);
}
public int traversal(TreeNode root ,int value){
if(root == null) return -1;
if(root.val>value) return root.val;
//寻找左右子节点,第一个大于自己的
int leftsum = traversal(root.left,value);
int rightsum = traversal(root.right,value);
//如果两棵子树均存在大于最小值的节点,那么返回较小的那一个
if(leftsum>=0&&rightsum>=0){
return Math.min(leftsum,rightsum);
}
//否则,其余情况均返回较大的那一个
else{return Math.max(leftsum,rightsum);}
}
}
leetcode-513-找树左下角的值
力扣-513
题目:给定一个二叉树,在树的最后一行找到最左边的值。
示例:
输入:
2
/ \
1 3
输出:
1
思路:DFS BFS
1.数据结构上的运用
DFS用递归的形式,用到了栈结构,先进后出。
BFS选取状态用队列的形式,先进先出。
2.复杂度
DFS的复杂度与BFS的复杂度大体一致,不同之处在于遍历的方式与对于问题的解决出发点不同,DFS适合目标明确,而BFS适合大范围的寻找。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int findBottomLeftValue(TreeNode root) {
//BFS 广度优先搜索
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
root = queue.poll();
if(root.right!=null) queue.offer(root.right);
if(root.left!=null) queue.offer(root.left);
}
return root.val;
}
}
class Solution {
int res;
int max = Integer.MIN_VALUE;
public int findBottomLeftValue(TreeNode root) { //dfs 深度优先搜索
dfs(root,0);
return res;
}
public void dfs(TreeNode node,int depth){
if(node != null){
if(node.left==null&&node.right==null){
if(max<depth){
max=depth;
res = node.val;
}
}
dfs(node.left,depth+1);
dfs(node.right,depth+1);
}
}
}
前中后遍历
leetcode-144-非递归实现二叉树前序遍历
力扣-144
史上最全遍历二叉树详解
题目:给定一个二叉树,返回它的 前序 遍历。(迭代、非递归遍历)
思路:利用栈,根—左—右,栈 根—右—左
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
if(node==null) continue;
ret.add(node.val);
if(node.right!=null){
stack.push(node.right);
}
if(node.left!=null){
stack.push(node.left);
}
}
return ret;
}
leetcode-145-非递归实现二叉树后序遍历
力扣-145
史上最全遍历二叉树详解
题目:给定一个二叉树,返回它的 后序 遍历。(迭代、非递归遍历)
思路:利用栈,根—右—左,然后反转 左—右—根
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
if(node==null) continue;
ret.add(node.val);
if(node.left!=null){
stack.push(node.left);
}
if(node.right!=null){
stack.push(node.right);
}
}
Collections.reverse(ret);
return ret;
}
}
leetcode-94-非递归实现二叉树中序遍历
力扣-94
题目:给定一个二叉树,返回它的 中序 遍历。(迭代、非递归遍历)
思路:利用栈
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur!=null||!stack.isEmpty()){
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
TreeNode node = stack.pop();
ret.add(node.val);
cur = node.right;
}
return ret;
}