思路【labuladong】
1)是否可以通过遍历一遍二叉树得到答案?如果可以,用一个traverse函数配合外部变量来实现——回溯
2)是否可以定义一个递归函数,通过子问题的答案推导出原问题的答案?如果可以,写出这个递归函数的定义,并充分利用这个函数的返回值——动态规划
*)如果单独抽出一个二叉树节点,它需要做什么事情?需要在什么时候(前/中/后序)做?
// 递归遍历
class Solution {
// 定义结果列表
List<Integer> result = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
// 启动递归
traverse(root);
return result;
}
public void traverse(TreeNode root) {
// 递归结束条件
if (root == null) {
return;
}
// 将root的值加入结果列表
result.add(root.val);
// 递归遍历左子树
traverse(root.left);
// 递归遍历右子树
traverse(root.right);
}
}
// 迭代:使用栈
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
// 保存结果列表
List<Integer> res = new ArrayList<Integer>();
// 借助栈实现迭代
Deque<TreeNode> stk = new LinkedList<TreeNode>();
while (root != null || !stk.isEmpty()) {
while (root != null) {
stk.push(root);
// 前序
res.add(root.val);
root = root.left;
}
root = stk.pop();
root = root.right;
}
return res;
}
}
// 递归
// class Solution {
// 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);
// }
// }
// 迭代
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
// 结果列表
List<Integer> result = new ArrayList<>();
// 栈
Deque<TreeNode> stack = new LinkedList<>();
// 迭代
while (root != null || !stack.isEmpty()) {
// 左下进栈
while (root != null) {
stack.push(root);
root = root.left;
}
// 中序
root = stack.pop();
result.add(root.val);
// 右子树存在入栈,不存在弹出栈中元素继续遍历
root = root.right;
}
return result;
}
}
// 递归
class Solution {
List<Integer> res = new ArrayList<Integer>();
public List<Integer> postorderTraversal(TreeNode root) {
traverse(root);
return res;
}
public void traverse(TreeNode root) {
if (root == null) {
return;
}
traverse(root.left);
traverse(root.right);
res.add(root.val);
}
}
// 迭代
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
Deque<TreeNode> stk = new LinkedList<TreeNode>();
// 用prev记录访问历史,看右子树是否已经被访问过,能否将其弹出
TreeNode prev = null;
while (root != null || !stk.isEmpty()) {
while (root != null) {
stk.push(root);
root = root.left;
}
root = stk.pop();
// 右子树已经被访问过
if (root.right == null || prev == root.right) {
res.add(root.val);
// 更新历史访问记录,标记该子树已经访问完了
prev = root;
// 回溯
root = null;
} else {
// 如果右子树没有被访问,将当前节点压栈,访问右子树
stk.push(root);
root = root.right;
}
}
return res;
}
}
4)二叉树的层序遍历
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
// 思路:定义一个队列,在弹出一个节点的同时将其左右子树加入;每层要放在同一个list中,计算长度确定
// 定义队列
Queue<TreeNode> q = new LinkedList<>();
// 定义结果集
List<List<Integer>> result = new ArrayList<List<Integer>>();
if (root == null) {
return result;
}
q.offer(root);
while (!q.isEmpty()) {
List<Integer> level = new ArrayList<>();
int len = q.size();
for (int i = 0; i < len; i++) {
TreeNode node = q.poll();
level.add(node.val);
if (node.left != null) {
q.offer(node.left);
}
if (node.right != null) {
q.offer(node.right);
}
}
result.add(level);
}
return result;
}
}
// 广度:每一层都是一个回文
// 深度:遍历顺序反一下,左子树是左中右,右子树是右中左
class Solution {
public boolean isSymmetric(TreeNode root) {
return check(root, root);
}
// 递归判断节点值是否相等
public boolean check(TreeNode leftNode, TreeNode rightNode) {
if (leftNode == null && rightNode == null) {
return true;
}
if (leftNode == null || rightNode == null) {
return false;
}
return (leftNode.val == rightNode.val) && check(leftNode.left, rightNode.right) && check(leftNode.right, rightNode.left);
}
}
// 广度优先
// class Solution {
// public int maxDepth(TreeNode root) {
// // 思路:层次遍历,每一层的时候,深度加1
// if (root == null) {
// return 0;
// }
// int deep = 0;
// Queue<TreeNode> q = new LinkedList<>();
// q.offer(root);
// while (!q.isEmpty()) {
// // deep ++;
// int len = q.size();
// System.out.println(len);
// for (int i = 0; i < len; i++) {
// TreeNode node = q.poll();
// if (node.left != null) {
// q.offer(node.left);
// }
// if (node.right != null) {
// q.offer(node.right);
// }
// }
// deep ++;
// }
// return deep;
// }
// }
// // 深度优先
// // 分解的思路
// class Solution {
// public int maxDepth(TreeNode root) {
// // 思路:递归,空节点退出,一个过程是左右子树的最大值+1
// if (root == null) {
// return 0;
// } else {
// int leftH = maxDepth(root.left);
// int rightH = maxDepth(root.right);
// return Math.max(leftH, rightH) + 1;
// }
// }
// }
// 遍历的思路
class Solution {
// 最大深度
int maxDepth = 0;
// 当前深度
int depth = 0;
public int maxDepth(TreeNode root) {
// 思路:递归,空节点退出,一个过程是左右子树的最大值+1
traverse(root);
return maxDepth;
}
public void traverse(TreeNode root) {
if (root == null) {
return;
}
depth++;
maxDepth = Math.max(maxDepth, depth);
traverse(root.left);
traverse(root.right);
depth--;
}
}
// 递归
// 分解
// class Solution {
// // 函数定义:root节点的最小深度
// public int minDepth(TreeNode root) {
// // 退出条件
// if (root == null) {
// return 0;
// }
// if (root.left == null) {
// return minDepth(root.right) + 1;
// }
// if (root.right == null) {
// return minDepth(root.left) + 1;
// }
// return Math.min(minDepth(root.left), minDepth(root.right)) + 1;
// }
// }
// 递归
class Solution {
// int depth = 0;
// int minDepth = Integer.MAX_VALUE;
public int minDepth(TreeNode root) {
int result = traverse(root);
return result;
}
public int traverse(TreeNode root) {
if (root == null) {
return 0;
}
if (root.left == null) {
return traverse(root.right) + 1;
}
if (root.right == null) {
return traverse(root.left) + 1;
}
int l = traverse(root.left);
int r = traverse(root.right);
return Math.min(l, r) + 1;
}
}
// // 遍历
// class Solution {
// int result;
// public int countNodes(TreeNode root) {
// traverse(root);
// return result;
// }
// public void traverse(TreeNode root) {
// if (root == null) {
// return;
// }
// // int l = traverse(root.left) + 1;
// // int r = traverse(root.right) + 1;
// traverse(root.left);
// traverse(root.right);
// result ++;
// // return l + r;
// }
// }
// 分解
class Solution {
// 函数定义:根节点的个数
public int countNodes(TreeNode root) {
if(root == null) {
return 0;
}
// 左节点的个数
int l = countNodes(root.left);
int r = countNodes(root.right);
return l + r + 1;
}
}
// // 遍历——自顶向下
// class Solution {
// // 左子树是平衡二叉树,右子树是平衡二叉树,左右子树高度差不超过1
// public boolean isBalanced(TreeNode root) {
// if (root == null) {
// return true;
// }
// boolean result = (Math.abs(traverse(root.left) - traverse(root.right)) <= 1);
// return isBalanced(root.left) && isBalanced(root.right) && result;
// }
// // 求深度
// public int traverse(TreeNode root) {
// if (root == null) {
// return 0;
// }
// int l = traverse(root.left);
// int r = traverse(root.right);
// return Math.max(l, r) + 1;
// }
// }
// 自底向上——这里用int返回值既解决了二叉树深度的问题,也同时可以判断是否是平衡二叉树
class Solution {
public boolean isBalanced(TreeNode root) {
return height(root) >= 0;
}
public int height(TreeNode root) {
if (root == null) {
return 0;
}
int leftHeight = height(root.left);
int rightHeight = height(root.right);
// 判断是否平衡
if (leftHeight == -1 || rightHeight == -1 || Math.abs(leftHeight - rightHeight) > 1) {
return -1;
} else {
return Math.max(leftHeight, rightHeight) + 1;
}
}
}
// 遍历,自顶向下,因为路径是从上到下的顺序
class Solution {
List<String> result = new ArrayList<>();
// 返回二叉树所有路径
public List<String> binaryTreePaths(TreeNode root) {
constructPath(root, "");
return result;
}
// 遍历二叉树
public void constructPath(TreeNode root, String path) {
if (root == null) {
return;
}
StringBuffer pathnow = new StringBuffer(path);
pathnow.append(Integer.toString(root.val));
if (root.left == null && root.right == null) {
result.add(pathnow.toString());
} else {
pathnow.append("->");
constructPath(root.left, pathnow.toString());
constructPath(root.right, pathnow.toString());
}
}
}
// 遍历:遍历到左节点的时候就加
class Solution {
// 和
int sum = 0;
public int sumOfLeftLeaves(TreeNode root) {
traverse(root, 0);
return sum;
}
public void traverse(TreeNode root, int flag) {
if (root == null) {
return;
}
if (root.left == null && root.right == null && flag == 1) {
sum += root.val;
}
traverse(root.left, 1);
traverse(root.right, 0);
}
}
// 遍历
class Solution {
int curVal = 0;
int curHeight = 0;
public int findBottomLeftValue(TreeNode root) {
traverse(root, 0);
return curVal;
}
public void traverse(TreeNode root, int height) {
if (root == null) {
return;
}
// 高度加1
height++;
traverse(root.left, height);
traverse(root.right, height);
// 记录下每层的第一个节点
if (height > curHeight) {
curHeight = height;
curVal = root.val;
}
}
}
// class Solution {
// public boolean hasPathSum(TreeNode root, int targetSum) {
// return traverse(root, targetSum, 0);
// }
// public boolean traverse (TreeNode root, int targetSum, int sum) {
// if (root == null) {
// return false;
// }
// sum += root.val;
// traverse(root.left, targetSum, sum);
// traverse(root.right, targetSum, sum);
// if (root.left == null && root.right == null && targetSum == sum) {
// return true;
// }
// }
// }
class Solution {
// 函数定义:root节点到叶子节点的路径和是sum
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);
}
}
// class Solution {
// // 翻转以root为根的二叉树,返回root节点
// public TreeNode invertTree(TreeNode root) {
// if (root == null) {
// return root;
// }
// // change(root);
// // 翻转左右节点
// TreeNode temp = root.left;
// root.left = root.right;
// root.right = temp;
// // 递归左右节点
// invertTree(root.left);
// invertTree(root.right);
// // change(root);
// return root;
// }
// public void change(TreeNode root) {
// TreeNode temp = root.left;
// root.left = root.right;
// root.right = temp;
// }
// }
class Solution {
// 翻转以root为根的二叉树,返回root节点
public TreeNode invertTree(TreeNode root) {
return traver(root);
}
public TreeNode traver(TreeNode root) {
if (root == null) {
return root;
}
// 交换左右节点
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
// 继续遍历
traver(root.left);
traver(root.right);
return root;
}
}
2)构造二叉树——需要画图搞清楚下标
// 前序的第一个节点是根节点,从中序中找到左右子树的分界点
class Solution {
// 由中序遍历构建一个HashMap,存放节点值与索引的对应
HashMap<Integer, Integer> valToIndex = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
// 填充map
for (int i = 0; i < inorder.length; i++) {
valToIndex.put(inorder[i], i);
}
// 递归构建二叉树
return build(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
}
public TreeNode build(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {
// 结束条件
if (preStart > preEnd) {
return null;
}
int rootVal = preorder[preStart];
int index = valToIndex.get(rootVal);
TreeNode root = new TreeNode(rootVal);
root.left = build(preorder, preStart + 1, preStart + (index - inStart), inorder, inStart, index - 1);
root.right = build(preorder, (preStart +index - inStart) + 1, preEnd, inorder, index + 1, inEnd);
return root;
}
}
class Solution {
// 定义一个map存放中序遍历中值到索引的映射,方便后续找左右节点分界
HashMap<Integer, Integer> valToIndex = new HashMap<>();
public TreeNode buildTree(int[] inorder, int[] postorder) {
// 遍历中序数组
for (int i = 0; i < inorder.length; i++) {
valToIndex.put(inorder[i], i);
}
// 递归构建二叉树
return build(inorder, 0, inorder.length - 1, postorder, 0, postorder.length - 1);
}
public TreeNode build (int[] inorder, int inStart, int inEnd, int[] postorder, int postStart, int postEnd) {
// 结束条件
if (inStart > inEnd) {
return null;
}
// root 节点的值就是后序遍历数组的最后一个元素
System.out.println(111);
int rootVal = postorder[postEnd];
// 找出rootVal在中序遍历中的索引,就可以分开左右子树
int index = valToIndex.get(rootVal);
TreeNode root = new TreeNode(rootVal);
root.left = build(inorder, inStart, index - 1, postorder, postStart, postStart + (index - inStart) - 1);
root.right = build(inorder, index + 1, inEnd, postorder, postStart + (index - inStart), postEnd - 1);
return root;
}
}
class Solution {
// 定义一个HashMap,存储postorder中值到索引的映射
HashMap<Integer, Integer> valToIndex = new HashMap<>();
public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {
// 填充HashMap
for (int i = 0; i < postorder.length; i++) {
valToIndex.put(postorder[i], i);
}
// 递归构造二叉树
return build(preorder, 0, preorder.length - 1, postorder, 0, postorder.length - 1);
}
// 递归构造二叉树的算法
public TreeNode build(int[] preorder, int preStart, int preEnd, int[] postorder, int postStart, int postEnd) {
// 递归结束条件
if (preStart > preEnd) {
return null;
}
// 只剩一个根节点
if (preStart == preEnd) {
return new TreeNode(preorder[preStart]);
}
// 根节点
int rootVal = preorder[preStart];
// 左子树的根节点
int leftRootVal = preorder[preStart + 1];
// 在后续遍历中寻找左子树根节点的位置,划分左右子树
int index = valToIndex.get(leftRootVal);
// 左子树的元素个数
int leftSize = index - postStart + 1;
// 构造根节点
TreeNode root = new TreeNode(rootVal);
root.left = build(preorder, preStart + 1, preStart + leftSize, postorder, postStart, postStart + leftSize);
root.right = build(preorder, preStart + leftSize + 1, preEnd, postorder, index + 1, postEnd - 1);
return root;
}
}
// 思路:计算数组的最大值,作为根节点,分别计算出左右的子数组,递归构建树
// 感想:增加参数个数,可以简化中间计算
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
// 本次数组的长度
int len = nums.length;
if (len == 0) {
return null;
}
// 计算本数组的最大值,以及最大值的位置(方便后续分割成两个子数组)
int maxVal = -1;
int maxIndex = -1;
for (int i = 0; i < len; i++) {
if (nums[i] > maxVal) {
maxVal = nums[i];
maxIndex = i;
}
}
// 给根节点赋值
TreeNode root = new TreeNode(maxVal);
// 左子树构建需要的子数组
int[] leftNums = new int[maxIndex];
for (int i = 0; i < maxIndex; i++) {
leftNums[i] = nums[i];
}
root.left = constructMaximumBinaryTree(leftNums);
// 右子树构建需要的子数组
int[] rightNums = new int[len - maxIndex - 1];
for (int i = 0; i < len - maxIndex - 1; i++) {
rightNums[i] = nums[maxIndex + 1 + i];
}
root.right = constructMaximumBinaryTree(rightNums);
return root;
// return build(nums, 0, nums.length - 1);
}
// public TreeNode build(int[] nums, int lo, int hi) {
// // int len = nums.length;
// // if (len == 0) {
// // return null;
// // }
// if (lo > hi) {
// return null;
// }
// int maxVal = -1;
// int maxIndex = -1;
// for (int i = lo; i <= hi; i++) {
// if (nums[i] > maxVal) {
// maxVal = nums[i];
// maxIndex = i;
// }
// }
// TreeNode root = new TreeNode(maxVal);
// root.left = build(nums, lo, maxIndex - 1);
// root.right = build(nums, maxIndex + 1, hi);
// return root;
// // int[] leftNums = new int[];
// // for (int i = 0; i <= hi - lo + 1; i++) {
// // leftNums[i] = nums[i + lo];
// // }
// // root.left = constructMaximumBinaryTree(leftNums);
// // int[] rightNums = new int[len - maxIndex - 1];
// // for (int i = 0; i < len - maxIndex - 1; i++) {
// // rightNums[i] = nums[len - maxIndex + 1 + i];
// // }
// // root.right = constructMaximumBinaryTree(rightNums);
// }
}
class Solution {
// 函数定义:合并两棵树
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if(root1 == null && root2 == null) {
return root1;
}
if (root1 == null && root2 != null) {
return root2;
}
if (root1 != null && root2 == null) {
return root1;
}
root1.val = root1.val + root2.val;
root1.left = mergeTrees(root1.left, root2.left);
root1.right = mergeTrees(root1.right, root2.right);
return root1;
}
}
// 思路一:遍历,新构造一个二叉树,不断增加节点——函数返回值为void——>希望在原本二叉树上进行操作
// 思路二:分解
class Solution {
// 函数定义:将以root节点为根的二叉树拉平
public void flatten(TreeNode root) {
if (root == null) {
return;
}
// 将左子树拉平
flatten(root.left);
// 将右子树拉平
flatten(root.right);
// 取到拉平后的左右子树
TreeNode letfNode = root.left;
TreeNode rightNode = root.right;
// 将左子树连接到root的右子树
root.left = null;
root.right = letfNode;
// 将原右子树拼接到尾部
TreeNode p = root;
while (p.right != null) {
p = p.right;
}
p.right = rightNode;
}
}
// 思路:左子树根节点 < 根节点 < 右子树根节点 && 左右子树都是二叉搜索树
class Solution {
// 记录上一个节点的值,初始值为long的最小值
long pre = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
return traverse(root);
}
public boolean traverse(TreeNode root) {
if (root == null) {
return true;
}
// 左子树
boolean leftT = traverse(root.left);
if (root.val <= pre) return false;
pre = root.val;
// 右子树
boolean rightT = traverse(root.right);
// 左子树是二叉搜索树,右子树是二叉搜索树,左子树 < 根 < 右子树
return leftT && rightT;
}
}
// 思路:遍历二叉树,利用额外的变量记录二叉树的累计和
class Solution {
int sum = 0;
public TreeNode convertBST(TreeNode root) {
traverse(root);
return root;
}
public void traverse(TreeNode root) {
if (root == null) {
return;
}
traverse(root.right);
sum += root.val;
root. val = sum;
traverse(root.left);
}
}
class Solution {
// 记录排名
int rank = 0;
// 记录结果
int result = 0;
public int kthSmallest(TreeNode root, int k) {
traverse(root, k);
return result;
}
// 中序遍历
public void traverse(TreeNode root, int k) {
if (root == null) {
return;
}
traverse(root.left, k);
rank++;
if (rank == k) {
result = root.val;
return;
}
traverse(root.right, k);
}
}
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 递归结束条件
if (root == null || root == p || root == q) {
return root;
}
// 后序遍历
TreeNode leftT = lowestCommonAncestor(root.left, p, q);
TreeNode rightT = lowestCommonAncestor(root.right, p, q);
// 如果没有找到节点p或q
if (leftT == null && rightT == null) {
return null;
} else if (leftT == null && rightT != null) {
return rightT;
} else if (leftT != null && rightT == null) {
return leftT;
} else {
return root;
}
}
}
// 思路:root应该在pq之间
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 如果根节点大于p、q,就往右子树里搜索
if (p.val > root.val && q.val > root.val) {
return lowestCommonAncestor(root.right, p, q);
}
// 如果根节点小于p、q,就往左子树里搜索
if (p.val < root.val && q.val < root.val) {
return lowestCommonAncestor(root.left, p, q);
}
return root;
}
}
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
TreeNode root = traverse(nums, 0, nums.length - 1);
return root;
}
public TreeNode traverse(int[] nums, int start, int end) {
if (start > end) {
return null;
}
int index = (start + end) / 2;
TreeNode root = new TreeNode(nums[index]);
root.left = traverse(nums, start, index - 1);
root.right = traverse(nums, index + 1, end);
return root;
}
}
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
// 递归结束条件
if (root == null) {
return null;
}
// 找到了要删除
if (root.val == key) {
// 1. 左右子树都为空, 直接删
if (root.left == null && root.right == null) {
return null;
}
// 2. 左子树为空,右子树接替;右子树为空,左子树接替
if (root.left == null) {
return root.right;
}
if (root.right == null) {
return root.left;
}
// 3. 左右子树都不为空,使用右子树的最小节点接替
TreeNode minNode = getMin(root.right);
root.right = deleteNode(root.right, minNode.val);
minNode.left = root.left;
minNode.right = root.right;
root = minNode;
} else if (root.val > key) {
// 要往小了找
root.left = deleteNode(root.left, key);
} else if (root.val < key) {
// 要往大了找
root.right = deleteNode(root.right, key);
}
return root;
}
// 找二叉搜索树中最小的节点
public TreeNode getMin(TreeNode root) {
while (root.left != null) {
root = root.left;
}
return root;
}
}
[未完待续……]