文章目录
- 2019-08-01(二叉树的中序遍历-94-medium)
- 2019-08-01(二叉树的前序遍历-144-medium)
- 2019-08-02(二叉树的后序遍历-145-easy)
- 2019-08-02(不同的二叉搜索树-96-medium)
- 2019-08-03(不同的二叉树II-95-medium)
- 2019-08-04(恢复二叉搜索树-99-hard)
- 2019-08-05(二叉树的层次遍历-102-medium)
- 2019-08-06(二叉树的锯齿形层次遍历-103-medium)
- 2019-08-07(从前序遍历与中序遍历构造二叉树-105-medium)
- 2019-08-07(从中序与后序遍历构造二叉树-106-medium)
- 2019-08-08~2019-08-13总共6天没有刷算法题!
- 2019-08-14(二叉树的层次遍历II-107-easy)
- 2019-08-14(有序链表转换成二叉搜索树-109-medium)
- 2019-08-15(填充每个节点的下一个右侧节点指针-116-medium)
- 2019-08-16(填充每个节点的下一个右侧节点指针II-117-medium)
- 2019-08-16(二叉树的最大路径和-124-hard)
- 2019-08-18(求根到叶子节点数字之和-129-medium)
- 2019-08-19(二叉树的右视图-199-medium)
- 2019-08-19(二叉搜索树迭代器-173-medium)
- 2019-08-20(完全二叉树的节点个数-222-medium)
- 2019-08-21(二叉树的最近公共祖先-236-medium)
- 2019-08-22(二叉树的序列化与反序列化-297-hard)
- 2019-08-23(打家劫舍III-337-medium)
- 2019-08-24(序列化与反序列化二叉搜索树-449-medium)
- 2019-08-25(删除二叉搜索树中的节点-450-medium)
- 2019-08-26(出现次数最多的子树元素和-508-medium)
- 2019-08-27(找树左下角的值-513-medium)
- 2019-08-27(在每个树行中找最大值-515-medium)
- 2019-08-28(在二叉树中增加一行-623-medium)
- 2019-08-29(寻找重复的子树-652-medium)
- 2019-08-30(最大二叉树-654-medium)
2019-08-01(二叉树的中序遍历-94-medium)
给定一个二叉树,返回它的中序 遍历。
示例:
输入: [1,null,2,3]
1
2
/
3
输出: [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-inorder-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 递归
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
helper(root, list);
return list;
}
// 左中右
public void helper(TreeNode root, List<Integer> list) {
if (root == null) return;
helper(root.left, list);
list.add(root.val);
helper(root.right, list);
}
// 迭代法
public List<Integer> inorderTraversal2(TreeNode root) {
List<Integer> list = new ArrayList<>();
if (root == null) return list;
Stack<TreeNode> stack = new Stack<>();
// 用一个指针模拟访问过程
TreeNode pre = root;
while(!stack.isEmpty() || pre != null){
while(pre != null){
stack.push(pre);
pre = pre.left;
}
TreeNode temp = stack.pop();
list.add(temp.val);
// 强制转移到右边,因为为null不会继续下去,所以无影响
pre = temp.right;
}
return list;
}
2019-08-01(二叉树的前序遍历-144-medium)
给定一个二叉树,返回它的 前序 遍历。
示例:
输入: [1,null,2,3]
1
2
/
3
输出: [1,2,3]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-preorder-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 递归
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
if(root == null) return list;
helper(root,list);
return list;
}
// 中左右
public void helper(TreeNode root, List<Integer> list){
if(root == null) return;
list.add(root.val);
helper(root.left,list);
helper(root.right,list);
}
// 迭代的方法
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
if (root == null) return list;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
list.add(node.val);
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
return list;
}
2019-08-02(二叉树的后序遍历-145-easy)
给定一个二叉树,返回它的 后序 遍历。
示例:
输入: [1,null,2,3]
1
2
/
3
输出: [3,2,1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-postorder-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 迭代法....有难度,每一次记录本节点与左右节点,分步骤弹出,满足条件才弹出,不然一直添加
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
if (root == null) return list;
Stack<TreeNode> stack = new Stack<>();
TreeNode pre = null;
stack.push(root);
while (!stack.isEmpty()) {
TreeNode temp = stack.peek();
if((temp.left == null && temp.right == null)
|| (pre != null && (pre == temp.right || pre == temp.left))){
list.add(temp.val);
pre = temp;
stack.pop();
}else{
// 分步骤处理的,记录每一个节点,符合条件的才弹出,否则一直添加
if(temp.right != null) stack.push(temp.right);
if(temp.left != null) stack.push(temp.left);
}
}
return list;
}
// 递归法
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
if(root == null) return list;
helper(root,list);
return list;
}
// 左右中
public void helper(TreeNode root, List<Integer> list){
if(root == null) return;
helper(root.left,list);
helper(root.right,list);
list.add(root.val);
}
2019-08-02(不同的二叉搜索树-96-medium)
给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \
3 2 1 1 3 2
/ / \
2 1 2 3
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-binary-search-trees
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 采用动态规划法解题
// G(n) = G(i-1)*G(n-i)
public int numTrees(int n) {
// 求不同的二叉搜索树的个数
int[] G = new int[n + 1];
G[0] = 1;
G[1] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= i; j++) {
G[i] += G[j - 1] * G[i - j];
}
}
return G[n];
}
2019-08-03(不同的二叉树II-95-medium)
给定一个整数 n,生成所有由 1 … n 为节点所组成的二叉搜索树。
示例:
输入: 3
输出:
[
[1,null,3,2],
[3,2,null,1],
[3,1,null,null,2],
[2,1,3],
[1,null,2,null,3]
]
解释:
以上的输出对应以下 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \
3 2 1 1 3 2
/ / \
2 1 2 3
在真实的面试中遇到过这道题?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-binary-search-trees-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解决本题的主要,根据二叉树的性质,左边小于节点,节点小于右边,如果左边大于右边,则左边添null,等于则只剩节点一个,然后左获取左子树,右获取右子树,最后组合。
public List<TreeNode> generateTrees(int n) {
List<TreeNode> res = new ArrayList<>();
if (n == 0) return res;
return getList(1, n);
}
public List<TreeNode> getList(int start, int end) {
List<TreeNode> res = new ArrayList<>();
// 当开始的节点小于技术的点的时候
if (start > end) {
res.add(null);
return res;
}
if (start == end) {
TreeNode node = new TreeNode(start);
res.add(node);
}
for (int i = start; i <= end; i++) {
List<TreeNode> nodeLeft = getList(start, i - 1);
List<TreeNode> nodeRight = getList(i + 1, end);
// 左边的情况和右边的情况相结合
for (TreeNode left : nodeLeft) {
for (TreeNode right : nodeRight) {
// 构造树
TreeNode root = new TreeNode(i);
root.left = left;
root.right = right;
res.add(root);
}
}
}
return res;
}
// 迭代法
public boolean isValidBST2(TreeNode root) {
if (root == null) return true;
Stack<TreeNode> stack = new Stack<>();
TreeNode pre = null;
TreeNode p = root;
while (!stack.isEmpty() || p != null) {
while (p != null) {
stack.push(p);
p = p.left;
}
p = stack.pop();
if (pre != null || pre.val >= p.val) return false;
pre = p;
p = p.right;
}
return true;
}
// 利用中序遍历递归
TreeNode p = null;
public boolean isValidBST3(TreeNode root) {
if (root == null) return true;
if (!isValidBST3(root.left)) return false;
if(p != null && p.val >= root.val) return false;
p = root;// p永远作为中间节点
return isValidBST3(root.right);
}
2019-08-04(恢复二叉搜索树-99-hard)
二叉搜索树中的两个节点被错误地交换。
请在不改变其结构的情况下,恢复这棵树。
示例 1:
输入: [1,3,null,null,2]
1
/
3
2
输出: [3,1,null,null,2]
3
/
1
2
示例 2:
输入: [3,1,4,null,null,2]
3
/
1 4
/
2
输出: [2,1,4,null,null,3]
2
/
1 4
/
3
进阶:
使用 O(n) 空间复杂度的解法很容易实现。
你能想出一个只使用常数空间的解决方案吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/recover-binary-search-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
public void recoverTree(TreeNode root) {
if (root == null) return;
Stack<TreeNode> stack = new Stack<>();
TreeNode pre = null;
TreeNode cur = root;
TreeNode wrongNode = null;
while (!stack.isEmpty() || cur != null) {
if (cur != null) {
stack.push(cur);
cur = cur.left;// 这样cur才会有null的情况
} else {
cur = stack.pop();
// 找第一个错误的节点
// 当前节点小于上一个节点时,上一个节点时错误节点
if (wrongNode == null && pre != null && cur.val < pre.val) {
wrongNode = pre;
}
// 寻找第二个节点,做交换
// 当当前节点大于错误节点的时候,当前节点的前节点就是错误节点
if (wrongNode != null && pre != null && cur.val >= pre.val) {
swap(wrongNode, pre);
break;
}
pre = cur;
cur = cur.right;
}
// 如果到最后还是没找到
if (wrongNode != null && pre != null && cur.val >= pre.val) {
swap(wrongNode, pre);
}
}
}
private void swap(TreeNode wrongNode, TreeNode pre) {
int temp = wrongNode.val;
wrongNode.val = pre.val;
pre.val = temp;
}
2019-08-05(二叉树的层次遍历-102-medium)
给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 利用队列先进先出迭代
public List<List<Integer>> levelOrder1(TreeNode root) {
List<List<Integer>> list = new ArrayList<>();
if (root == null) return list;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
List<Integer> res = new ArrayList<>();
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode temp = queue.poll();
res.add(temp.val);
if (temp.left != null) {
queue.add(temp.left);
}
if (temp.right != null) {
queue.add(temp.right);
}
}
list.add(res);
}
return list;
}
List<List<Integer>> list = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
if (root == null) return list;
helper(root, 0);
return list;
}
// 利用层数,递归
public void helper(TreeNode root, int level) {
if (root == null) return;
if (list.size() == level) {
list.add(new ArrayList<>());
}
// 记录层数
list.get(level).add(root.val);
if (root.left != null) {
helper(root.left, level + 1);
}
if (root.right != null) {
helper(root.right, level + 1);
}
}
// 利用层次迭代
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> list = new ArrayList<>();
if (root == null) return list;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int level = 0;
while (!queue.isEmpty()) {
// 放入一个空的
list.add(new ArrayList<>());
int level_length = queue.size();
for (int i = 0; i < level_length; i++) {
TreeNode temp = queue.remove();
// 根据获取的层次来迭代
list.get(level).add(temp.val);
if (temp.left != null) {
queue.add(temp.left);
}
if (temp.right != null) {
queue.add(temp.right);
}
}
level++;
}
return list;
}
2019-08-06(二叉树的锯齿形层次遍历-103-medium)
给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回锯齿形层次遍历如下:
[
[3],
[20,9],
[15,7]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 链表+翻转符号
public List<List<Integer>> zigzagLevelOrder1(TreeNode root) {
List<List<Integer>> list = new ArrayList<>();
if (root == null) return list;
Queue<TreeNode> queue = new LinkedList<>();
boolean flag = true;
queue.add(root);
while (!queue.isEmpty()) {
LinkedList<Integer> res = new LinkedList<>();
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode temp = queue.poll();
if (flag) {
res.add(temp.val);
} else {
// 主要是这里,利用了数据结构的特点
res.addFirst(temp.val);
}
if (temp.left != null) {
queue.add(temp.left);
}
if (temp.right != null) {
queue.add(temp.right);
}
}
flag = !flag;
list.add(res);
}
return list;
}
// 递归--主要是利用的数据结构的特点
public List<List<Integer>> zigzagLevelOrder3(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
helper(res, root, 0);
return res;
}
public void helper(List<List<Integer>> res, TreeNode root, int depth) {
if (root == null) return;
if (list.size() == depth) {
list.add(new ArrayList<>());
}
// 判断反转
if (depth % 2 == 0) {
res.get(depth).add(root.val);
} else {
// 只要写0,添加,那么后面的自然就会把前面的给移动到后面去
res.get(depth).add(0, root.val);
}
// 什么时候反转
if (root.left != null) {
helper(res, root.left, depth + 1);
}
if (root.right != null) {
helper(res, root.right, depth + 1);
}
}
// 利用迭代来处理
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int level = 0;
while (!queue.isEmpty()) {
res.add(new ArrayList<>());
int cnt = queue.size();
for (int i = 0; i < cnt; i++) {
TreeNode temp = queue.remove();
if (level % 2 == 0) {
res.get(level).add(temp.val);
} else {
res.get(level).add(0, temp.val);
}
if (temp.left != null) {
queue.add(temp.left);
}
if (temp.right != null) {
queue.add(temp.right);
}
}
level++;
}
return res;
}
// 跟上面几乎一样
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int level = 0;
while (!queue.isEmpty()) {
List<Integer> list = new LinkedList<>();
int cnt = queue.size();
for (int i = 0; i < cnt; i++) {
TreeNode temp = queue.remove();
if (level % 2 == 0) {
list.add(temp.val);
} else {
list.add(0, temp.val);
}
if (temp.left != null) {
queue.add(temp.left);
}
if (temp.right != null) {
queue.add(temp.right);
}
}
res.add(list);
level++;
}
return res;
}
2019-08-07(从前序遍历与中序遍历构造二叉树-105-medium)
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/
9 20
/
15 7
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 递归方法
public TreeNode buildTree1(int[] preorder, int[] inorder) {
// 找出前序遍历和中序遍历的共同点
// 3 9 20 15 7
// 9 3 15 20 7
return helper(preorder, 0, inorder, 0, inorder.length);
}
public TreeNode helper(int[] preorder, int p, int[] inorder, int i, int j) {
if (i >= j) return null;
TreeNode root = new TreeNode(preorder[p]);
int k = 0;
while (inorder[k] != root.val) {
k++;
}
root.left = helper(preorder, p + 1, inorder, i, k);
// p+1+k-i p+1是前序遍历中的头结点,k-i表示左边走了多少个,这样右边从这走就正好
root.right = helper(preorder, p + 1 + k - i, inorder, k + 1, j);
return root;
}
// 递归 使用HashMap来保存中序的节点
public TreeNode buildTree(int[] preorder, int[] inorder) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return rebuild(preorder, 0, preorder.length, inorder, 0, inorder.length, map);
}
public TreeNode rebuild(int[] preorder, int p_start, int p_end, int[] inorder, int i_start, int i_end, HashMap<Integer, Integer> map) {
if(p_start == p_end) return null;
TreeNode root = new TreeNode(preorder[p_start]);
int i_index = map.get(root.val);
int leftNum = i_index - i_start;
// p_start+1,下一个,构建左边的话,从p_start+1开始,到p_start+1+leftNum结束
// 对应中序,从i_start开始,到i_index结束
root.left = rebuild(preorder,p_start+1,p_start+1+leftNum,inorder,i_start,i_index,map);
// 前序从p_start+1开始,到p_start+1+leftNum结束,中序从i_index+1开始,到i_end结束
root.right = rebuild(preorder,p_start+1+leftNum,p_end,inorder,i_index+1,i_end,map);
return root;
}
2019-08-07(从中序与后序遍历构造二叉树-106-medium)
根据一棵树的中序遍历与后序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:
3
/
9 20
/
15 7
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 与上面的例子相同,只是后序遍历从后面开始是终点,先建立右子树,再建立左子树
public TreeNode buildTree(int[] inorder, int[] postorder) {
return rebuild(inorder, 0, inorder.length, postorder, postorder.length - 1);
}
public TreeNode rebuild(int[] inorder, int i, int j, int[] postorder, int p) {
if (i == j) return null;
TreeNode root = new TreeNode(postorder[p]);
int k = 0;
while (inorder[k] != root.val) k++;
// 先构造右子树
root.right = rebuild(inorder, k + 1, j, postorder, p - 1);
// 再构造左子树
root.left = rebuild(inorder, i, k, postorder, p-1-(j-k-1));
return root;
}
2019-08-08~2019-08-13总共6天没有刷算法题!
2019-08-14(二叉树的层次遍历II-107-easy)
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其自底向上的层次遍历为:
[
[15,7],
[9,20],
[3]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 递归解决
public List<List<Integer>> levelOrderBottom1(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
helper(root, 0, res);
return res;
}
public void helper(TreeNode root, int depth, List<List<Integer>> res) {
if (root == null) return;
if (res.size() <= depth) {
res.add(0, new ArrayList<>());
}
// 0 1 2 3 4
// 4 3 2 1 0
res.get(res.size() - 1 - depth).add(root.val);
helper(root.left, depth + 1, res);
helper(root.right, depth + 1, res);
}
// BFS迭代
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
LinkedList<Integer> subList = new LinkedList<>();
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.remove();
if (node != null) {
subList.add(node.val);
queue.add(node.left);
queue.add(node.right);
}
}
if (subList.size() > 0) {
res.add(0, subList);
}
}
return res;
}
2019-08-14(有序链表转换成二叉搜索树-109-medium)
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定的有序链表: [-10, -3, 0, 5, 9],
一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/convert-sorted-list-to-binary-search-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 依次寻找中点即可,每次的中点作为树的节点即可
public TreeNode sortedListToBST(ListNode head) {
if(head == null) return null;
ListNode mid = findListNodeMid(head);
TreeNode res = new TreeNode(mid.val);
if(mid == head){
return res;
}
res.left = sortedListToBST(head);
res.right = sortedListToBST(mid.next);
return res;
}
public ListNode findListNodeMid(ListNode head){
ListNode pre = null;
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
pre = slow;
slow = slow.next;
fast = fast.next.next;
}
if(pre != null){
pre.next = null;
}
return slow;
}
2019-08-15(填充每个节点的下一个右侧节点指针-116-medium)
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
示例:
输入:{“KaTeX parse error: Expected '}', got 'EOF' at end of input: …":"1","left":{"id”:“2”,“left”:{“KaTeX parse error: Expected 'EOF', got '}' at position 53: …t":null,"val":4}̲,"next":null,"r…id”:“4”,“left”:null,“next”:null,“right”:null,“val”:5},“val”:2},“next”:null,“right”:{“KaTeX parse error: Expected '}', got 'EOF' at end of input: …":"5","left":{"id”:“6”,“left”:null,“next”:null,“right”:null,“val”:6},“next”:null,“right”:{"$id":“7”,“left”:null,“next”:null,“right”:null,“val”:7},“val”:3},“val”:1}
输出:{“KaTeX parse error: Expected '}', got 'EOF' at end of input: …":"1","left":{"id”:“2”,“left”:{“KaTeX parse error: Expected '}', got 'EOF' at end of input: …:null,"next":{"id”:“4”,“left”:null,“next”:{“KaTeX parse error: Expected '}', got 'EOF' at end of input: …:null,"next":{"id”:“6”,“left”:null,“next”:null,“right”:null,“val”:7},“right”:null,“val”:6},“right”:null,“val”:5},“right”:null,“val”:4},“next”:{“KaTeX parse error: Expected '}', got 'EOF' at end of input: …":"7","left":{"ref”:“5”},“next”:null,“right”:{“KaTeX parse error: Expected 'EOF', got '}' at position 9: ref":"6"}̲,"val":3},"righ…ref”:“4”},“val”:2},“next”:null,“right”:{"$ref":“7”},“val”:1}
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。
提示:
你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 递归写法
public Node connect(Node root) {
// 让这个指针指向其下一个节点的右侧节点
if (root == null) return null;
if (root.left != null) {
// 节点左节点下一个节点对应节点右节点
root.left.next = root.right;
// 判断节点下一个节点是否为空
if (root.next != null) {
// 不为空则将右节点的下一个节点与另一个节点的左节点相连接
root.right.next = root.next.left;
}
}
connect(root.left);
connect(root.right);
return root;
}
// 迭代写法
public Node connect1(Node root) {
if (root == null) return null;
Node pre = root;
while (pre != null) {
// 用一个临时节点
Node cur = pre;
while (cur != null) {
// 左边不为null,则选择下一个节点为右节点
if(cur.left != null) cur.left.next = cur.right;
// 右边不为null,且下一节点存在,则选择下一节点为next的左节点
if (cur.right != null && cur.next != null) cur.right.next = cur.next.left;
// 实质是在往右边前进
cur = cur.next;
}
// 每次向左边走
pre = pre.left;
}
return root;
}
2019-08-16(填充每个节点的下一个右侧节点指针II-117-medium)
给定一个二叉树
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
示例:
输入:{“KaTeX parse error: Expected '}', got 'EOF' at end of input: …":"1","left":{"id”:“2”,“left”:{“KaTeX parse error: Expected 'EOF', got '}' at position 53: …t":null,"val":4}̲,"next":null,"r…id”:“4”,“left”:null,“next”:null,“right”:null,“val”:5},“val”:2},“next”:null,“right”:{“KaTeX parse error: Expected '}', got 'EOF' at end of input: …null,"right":{"id”:“6”,“left”:null,“next”:null,“right”:null,“val”:7},“val”:3},“val”:1}
输出:{“KaTeX parse error: Expected '}', got 'EOF' at end of input: …":"1","left":{"id”:“2”,“left”:{“KaTeX parse error: Expected '}', got 'EOF' at end of input: …:null,"next":{"id”:“4”,“left”:null,“next”:{“KaTeX parse error: Expected 'EOF', got '}' at position 53: …t":null,"val":7}̲,"right":null,"…id”:“6”,“left”:null,“next”:null,“right”:{“KaTeX parse error: Expected 'EOF', got '}' at position 9: ref":"5"}̲,"val":3},"righ…ref”:“4”},“val”:2},“next”:null,“right”:{"$ref":“6”},“val”:1}
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。
提示:
你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
public Node connect(Node root) {
if (root == null) return null;
if (root.left != null) {
if (root.right != null) {
root.left.next = root.right;
} else {
root.left.next = findLeftChild(root);
}
}
if(root.right != null){
root.right.next = findLeftChild(root);
}
// 先构造右子树,因为找孩子节点从右边开始
connect(root.right);
connect(root.left);
return root;
}
private Node findLeftChild(Node root) {
while(root.next != null){
if(root.next.left != null) return root.next.left;
if(root.next.right != null) return root.next.right;
root = root.next;
}
return null;
}
2019-08-16(二叉树的最大路径和-124-hard)
给定一个非空二叉树,返回其最大路径和。
本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。
示例 1:
输入: [1,2,3]
1
/ \
2 3
输出: 6
示例 2:
输入: [-10,9,20,null,null,15,7]
-10
/
9 20
/
15 7
输出: 42
-源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-maximum-path-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 递归
int Max_num = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
getMaxNum(root);
return Max_num;
}
public int getMaxNum(TreeNode root) {
if (root == null) return 0;
int left_num = Math.max(getMaxNum(root.left), 0);
int right_num = Math.max(getMaxNum(root.right), 0);
// 新节点
int pre_num = root.val + left_num + right_num;
// 得出最大的值
Max_num = Math.max(pre_num, Max_num);
// 因为路径不能交叉,只能获取左右最大节点+父节点的值传回
return Math.max(left_num, right_num) + root.val;
}
2019-08-18(求根到叶子节点数字之和-129-medium)
给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。
例如,从根到叶子节点路径 1->2->3 代表数字 123。
计算从根到叶子节点生成的所有数字之和。
说明: 叶子节点是指没有子节点的节点。
示例 1:
输入: [1,2,3]
1
/
2 3
输出: 25
解释:
从根到叶子节点路径 1->2 代表数字 12.
从根到叶子节点路径 1->3 代表数字 13.
因此,数字总和 = 12 + 13 = 25.
示例 2:
输入: [4,9,0,5,1]
4
/
9 0
/
5 1
输出: 1026
解释:
从根到叶子节点路径 4->9->5 代表数字 495.
从根到叶子节点路径 4->9->1 代表数字 491.
从根到叶子节点路径 4->0 代表数字 40.
因此,数字总和 = 495 + 491 + 40 = 1026.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sum-root-to-leaf-numbers
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 字符串拼接
int sum = 0;
public int sumNumbers1(TreeNode root) {
helper(root, new StringBuilder());
return sum;
}
public void helper(TreeNode root, StringBuilder sb) {
if (root == null) return;
sb.append(root.val);
// 判断是否是叶子节点,是叶子节点就加入到sum中
if (root.left == null && root.right == null) {
sum += Integer.parseInt(sb.toString());
}
helper(root.left, sb);
helper(root.right, sb);
// 回退的操作没做--因为sb是全局变量,不回退的话是没法继续运算的
sb.delete(sb.length() - 1, sb.length());
}
// 直接数值运算
int res = 0;
public int sumNumbers(TreeNode root) {
newHelper(root, 0);
return res;
}
public void newHelper(TreeNode root, int temp) {
if (root == null) return;
if (root.left == null && root.right == null) {
sum += temp * 10 + root.val;
}
newHelper(root.left, temp * 10 + root.val);
newHelper(root.right, temp * 10 + root.val);
}
2019-08-19(二叉树的右视图-199-medium)
给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
示例:
输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]
解释:
1 <—
/
2 3 <—
\
5 4 <—
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-right-side-view
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 层序遍历
public List<Integer> rightSideView1(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) return res;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
List<Integer> list = new ArrayList<>();
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode temp = queue.remove();
list.add(temp.val);
if (temp.left != null) queue.add(temp.left);
if (temp.right != null) queue.add(temp.right);
}
// 添加最右边的元素即可
res.add(list.get(list.size() - 1));
}
return res;
}
// 递归解法
public List<Integer> rightSideView(TreeNode root) {
List<Integer> res = new ArrayList<>();
helper(root, res, 0);
return res;
}
private void helper(TreeNode root, List<Integer> res, int depth) {
if (root == null) return;
if (depth == res.size()) {
res.add(root.val);
}
// 因为是右视图,所以先遍历右边,只要个数与高度相等就添加
// 因为这里恰好是一层只放入一个数
helper(root.right, res, depth + 1);
helper(root.left, res, depth + 1);
}
2019-08-19(二叉搜索树迭代器-173-medium)
实现一个二叉搜索树迭代器。你将使用二叉搜索树的根节点初始化迭代器。
调用 next() 将返回二叉搜索树中的下一个最小的数。
示例:
BSTIterator iterator = new BSTIterator(root);
iterator.next(); // 返回 3
iterator.next(); // 返回 7
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 9
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 15
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 20
iterator.hasNext(); // 返回 false
提示:
next() 和 hasNext() 操作的时间复杂度是 O(1),并使用 O(h) 内存,其中 h 是树的高度。
你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 中至少存在一个下一个最小的数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-search-tree-iterator
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
Stack<TreeNode> stack = new Stack<>();
public BSTIterator(TreeNode root) {
if (root == null) return;
stack.push(root);
// 寻找到最左的节点
while (root.left != null) {
stack.push(root.left);
root = root.left;
}
}
/**
* @return the next smallest number
*/
public int next() {
// 弹出最左的节点
TreeNode temp = stack.pop();
if (temp.right != null) {
stack.push(temp.right);
TreeNode node = temp.right;
while (node.left != null) {
stack.push(node.left);
node = node.left;
}
}
return temp.val;
}
/**
* @return whether we have a next smallest number
*/
public boolean hasNext() {
// 根据栈中是否有元素判断是否有下一个
return !stack.isEmpty();
}
2019-08-20(完全二叉树的节点个数-222-medium)
给出一个完全二叉树,求出该树的节点个数。
说明:
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
示例:
输入:
1
/
2 3
/ \ /
4 5 6
输出: 6
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-complete-tree-nodes
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 左右子树,如果高度相等,则左边满树(前提加上父节点),统计右边即可
// 高度不相等,则右边树满树,统计左节点
public int countNodes(TreeNode root) {
if (root == null) return 0;
int left = getLevel(root.left);
int right = getLevel(root.right);
if (left == right) {
return countNodes(root.right) + (1 << left);
}else{
return countNodes(root.left) + (1 << right);
}
}
public int getLevel(TreeNode root) {
int level = 0;
while (root != null) {
level += 1;
root = root.left;
}
return level;
}
// 左右递归
int sum = 1;
public int countNodes2(TreeNode root) {
if (root == null) return 0;
if (root.left != null) {
sum += 1;
}
if (root.right != null) {
sum += 1;
}
countNodes2(root.left);
countNodes2(root.right);
return sum;
}
// 直接左右+1父节点个数
public int countNodes4(TreeNode root) {
if (root == null) return 0;
return countNodes4(root.left) + countNodes4(root.right) + 1;
}
2019-08-21(二叉树的最近公共祖先-236-medium)
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路:对二叉树做后序遍历,回溯:
回溯时:捕获mid,即当前节点是否为p或q;
当 left right mid 三个中有两个为True时,说明当前节点是最近的公共节点,记录至res;
返回值:左子树或右子树或当前节点中包含p或q;
最终,返回最近公共节点res。
// 回溯法
TreeNode res = null;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
recurseTree(root, p, q);
return res;
}
// 上面思路一致,三者中有两个满足则此时祖先节点就出来了
public boolean recurseTree(TreeNode curNode, TreeNode p, TreeNode q) {
if (curNode == null) return false;
int left = this.recurseTree(curNode.left, p, q) ? 1 : 0;
int right = this.recurseTree(curNode.right, p, q) ? 1 : 0;
int mid = (curNode == p || curNode == q) ? 1 : 0;
if (mid + left + right > 2) {
res = curNode;
}
return (mid + left + right) > 0;
}
// 递归,判断左右是否含有这个点,与二叉搜索树的祖先节点查找比较类似
// 不过那个按照大小来,这个按照是否存在来
public TreeNode lowestCommonAncestor1(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) return null;
if (isExist(root.left, p) && isExist(root.left, q)) {
return lowestCommonAncestor1(root.left, p, q);
} else if (isExist(root.right, p) && isExist(root.right, q)) {
return lowestCommonAncestor1(root.right, p, q);
} else {
return root;
}
}
public boolean isExist(TreeNode node, TreeNode cur) {
if (node == null) return false;
if (node.val == cur.val) {
return true;
}
return isExist(node.left, cur) || isExist(node.right, cur);
}
2019-08-22(二叉树的序列化与反序列化-297-hard)
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
示例:
你可以将以下二叉树:
1
/
2 3
/
4 5
序列化为 “[1,2,3,null,null,4,5]”
提示: 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
说明: 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder helper = helper(root, new StringBuilder());
return helper.toString();
}
public StringBuilder helper(TreeNode root, StringBuilder s) {
if (root == null) {
s.append("null,");
return s;
} else {
// 添加进入
s.append(root.val).append(",");
// 左边,
s = helper(root.left, s);
// 右边
s = helper(root.right, s);
}
return s;
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
String[] split = data.split(",");
LinkedList<String> strings = new LinkedList<>(Arrays.asList(split));
return redeserialize(strings);
}
public TreeNode redeserialize(List<String> s) {
if (s.get(0).equals("null")) {
s.remove(0);
return null;
}
TreeNode root = new TreeNode(Integer.valueOf(s.get(0)));
// 移除
s.remove(0);
root.left = redeserialize(s);
root.right = redeserialize(s);
return root;
}
2019-08-23(打家劫舍III-337-medium)
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
示例 1:
输入: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
示例 2:
输入: [3,4,5,1,3,null,1]
3
/ \
4 5
/ \ \
1 3 1
输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.
来源:力扣(LeetCode)
-接:https://leetcode-cn.com/problems/house-robber-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 动态规划需要推出公式,然后把公式写出来即可
public int rob(TreeNode root) {
if (root == null) return 0;
int[] res = doRob(root);
return Math.max(res[0], res[1]);
}
public int[] doRob(TreeNode root) {
int[] res = new int[2];
if (root == null) return res;
int[] left = doRob(root.left);
int[] right = doRob(root.right);
// 不包括根节点的,就第一层字子节点的值
res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
// 包括根节点的,和不包括根节点的二层子节点的值
res[1] = left[0] + right[0] + root.val;
return res;
}
2019-08-24(序列化与反序列化二叉搜索树-449-medium)
序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。
设计一个算法来序列化和反序列化二叉搜索树。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。
编码的字符串应尽可能紧凑。
注意:不要使用类成员/全局/静态变量来存储状态。 你的序列化和反序列化算法应该是无状态的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/serialize-and-deserialize-bst
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 利用二叉搜索树的性质
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if (root == null) return "";
String s = root.val + ",";
s += serialize(root.left);
s += serialize(root.right);
return s;
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if (data == null || data.length() == 0) return null;
String[] split = data.split(",");
return builderTree(split, 0, split.length - 1);
}
public TreeNode builderTree(String[] strs, int l, int r) {
if (l > r) {
return null;
}
// 构建根节点
TreeNode root = new TreeNode(Integer.valueOf(strs[l]));
int index = r+1;
for (int i = l+1; i <= r; i++) {
// 找到第一个比根节点大的,作为下一个节点
if (Integer.valueOf(strs[i]) > root.val) {
index = i;
break;
}
}
// 构建左右子树
root.left = builderTree(strs, l + 1, index - 1);
root.right = builderTree(strs, index, r);
return root;
}
// 二叉树通用
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder res = new StringBuilder();
return reserialize(root,res).toString();
}
public StringBuilder reserialize(TreeNode root,StringBuilder res){
if(root == null){
return res.append("null,");
}
res.append(root.val).append(",");
res = reserialize(root.left,res);
res = reserialize(root.right,res);
return res;
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
String[] split = data.split(",");
List<String> list = new LinkedList<>(Arrays.asList(split));
return redeserialize(list);
}
public TreeNode redeserialize(List<String> list){
if(list.get(0).equals("null")){
list.remove(0);
return null;
}
TreeNode res = new TreeNode(Integer.parseInt(list.get(0)));
list.remove(0);
res.left = redeserialize(list);
res.right = redeserialize(list);
return res;
}
2019-08-25(删除二叉搜索树中的节点-450-medium)
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。
示例:
root = [5,3,6,2,4,null,7]
key = 3
5
/
3 6
/ \
2 4 7
给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
5
/
4 6
/
2 7
另一个正确答案是 [5,2,6,null,4,null,7]。
5
/
2 6
\
4 7
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/delete-node-in-a-bst
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 左子树拿到最大的
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return null;
// key小,走左边
if(root.val > key){
root.left = deleteNode(root.left,key);
return root;
}
// key大,走右边
if(root.val < key){
root.right = deleteNode(root.right,key);
return root;
}
assert root.val == key;
// 如果左边为null,则直接将右边怼过来即可
if(root.left == null){
TreeNode right = root.right;
root.right = null;
return right;
}
// 如果右边为null,则直接将左边怼过来
if(root.right == null){
TreeNode left = root.left;
root.left = null;
return left;
}
// 如果左右都不为null,此时就需要进行前驱节点的查找并替换
// 查找左边最大的节点
TreeNode predecessorNode = maxNode(root.left);
// 复制一份作为新的节点
TreeNode predecessorNodeCopy = new TreeNode(predecessorNode.val);
// 将最大的节点从树中删除
predecessorNodeCopy.left = removeMaxNode(root.left);
// 右节点直接拿root的右节点即可
predecessorNodeCopy.right = root.right;
// 删除key节点
root.left = null;
root.right = null;
return predecessorNodeCopy;
}
// 删除的方法
private TreeNode removeMaxNode(TreeNode node) {
// 这是新节点的左边,也是此节点的左边
if(node.right == null){
TreeNode left = node.left;
node.left = null;
return left;
}
node.right = removeMaxNode(node.right);
return node;
}
private TreeNode maxNode(TreeNode node) {
if(node.right == null){
return node;
}
return maxNode(node.right);
}
// 类似于链表,可以利用链表的性质来删除节点
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return null;
// 判断根节点是否为key
if (root.val == key) {
// 如果右子树为null,直接返回左子树
if (root.right == null) {
return root.left;
} else {
// 右子树不为null,则获取右子树最小的节点来当做根节点
TreeNode node = root.right;
// 父节点
TreeNode parent = root;
// 需要移动寻找右子树最小的节点
while (node != null && node.left != null) {
// 父节点跟随移动(目的就是为了后面的删除)
parent = node;
node = node.left;
}
// 此时node已经是最小的节点了,此时需要进行值的交换
// 如果父的左节点为最小的,那么父节点的右节点就为null此时node.right=null
if (parent.left == node) parent.left = node.right;
if (parent.right == node) parent.right = node.right;
// 最后删除节点
node.left = root.left;
node.right = root.right;
return node;
}
} else if (root.val > key) {
// 左边
root.left = deleteNode(root.left, key);
} else {
// 右边
root.right = deleteNode(root.right, key);
}
return root;
}
2019-08-26(出现次数最多的子树元素和-508-medium)
给出二叉树的根,找出出现次数最多的子树元素和。一个结点的子树元素和定义为以该结点为根的二叉树上所有结点的元素之和(包括结点本身)。然后求出出现次数最多的子树元素和。如果有多个元素出现的次数相同,返回所有出现次数最多的元素(不限顺序)。
示例 1
输入:
5
/
2 -3
返回 [2, -3, 4],所有的值均只出现一次,以任意顺序返回所有值。
示例 2
输入:
5
/
2 -5
返回 [2],只有 2 出现两次,-5 只出现 1 次。
提示: 假设任意子树元素和均可以用 32 位有符号整数表示。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/most-frequent-subtree-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
int max = 0;
public int[] findFrequentTreeSum(TreeNode root) {
if(root == null) return new int[0];
HashMap<Integer,Integer> map = new HashMap<>();
helper(root, map);
int[] res = new int[map.size()];
int num = 0;
// 遍历map,获取键值
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if(entry.getValue() == max){
res[num++] = entry.getKey();
}
}
// 截取所需要的值
return Arrays.copyOfRange(res,0,num);
}
// 递归获取和,全局变量max统一最大这个概念
public int helper(TreeNode root,Map<Integer,Integer> map){
if(root == null) return 0;
// 左边
int left = helper(root.left,map);
// 右边
int right = helper(root.right,map);
// 节点值
int value = left + right + root.val;
// 存储元素值与次数
map.put(value,map.getOrDefault(value,0)+1);
// 获取最大次数
max = Math.max(max,map.get(value));
return value;
}
2019-08-27(找树左下角的值-513-medium)
给定一个二叉树,在树的最后一行找到最左边的值。
示例 1:
输入:
2
/ \
1 3
输出:
1
示例 2:
输入:
1
/ \
2 3
/ / \
4 5 6
/
7
输出:
7
注意: 您可以假设树(即给定的根节点)不为 NULL。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-bottom-left-tree-value
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 也可以使用迭代,一个全局变量保存第一个元素(这种方法等同迭代)
List<List<Integer>> res = new ArrayList<>();
public int findBottomLeftValue1(TreeNode root) {
helper(root, 0);
// 获取最后一个list的第一个元素即可
return res.get(res.size()).get(0);
}
public void helper(TreeNode root, int depth) {
if (root == null) return;
if (res.size() == depth) {
res.add(new ArrayList<>());
}
res.get(depth).add(root.val);
helper(root, depth + 1);
helper(root, depth + 1);
}
// 标准递归方法
int maxDepth = -1,maxLeft = -1;
public int findBottomLeftValue(TreeNode root) {
find(root, 0);
return maxLeft;
}
// 使用中序遍历,左中右顺序
private void find(TreeNode root, int depth) {
if(root == null) return;
// 左
find(root.left,depth + 1);
if(depth > maxDepth){
maxLeft = root.val;
}
find(root.right,depth+1);
}
2019-08-27(在每个树行中找最大值-515-medium)
您需要在二叉树的每一行中找到最大的值。
示例:
输入:
1
/ \
3 2
/ \ \
5 3 9
输出: [1, 3, 9]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
// 层序遍历
public List<Integer> largestValues1(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) return res;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
int size = queue.size();
// 记录这一行中的最大值
int max = Integer.MIN_VALUE;
for (int i = 0; i < size; i++) {
TreeNode node = queue.remove();
max = Math.max(node.val, max);
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}
res.add(max);
}
return res;
}
// 递归:深度优先搜索
List<Integer> list = new ArrayList<>();
public List<Integer> largestValues(TreeNode root) {
helper(root, 0);
return list;
}
private void helper(TreeNode root, int depth) {
if (root == null) return;
// 先把头结点值存入
if (depth + 1 > list.size()) {
list.add(depth, root.val);
}
// 判断同一行的数最大
if (root.val > list.get(depth)) {
list.set(depth, root.val);
}
helper(root.left, depth + 1);
helper(root.right, depth + 1);
}
2019-08-28(在二叉树中增加一行-623-medium)
给定一个二叉树,根节点为第1层,深度为 1。在其第 d 层追加一行值为 v 的节点。
添加规则:给定一个深度值 d (正整数),针对深度为 d-1 层的每一非空节点 N,为 N 创建两个值为 v 的左子树和右子树。
将 N 原先的左子树,连接为新节点 v 的左子树;将 N 原先的右子树,连接为新节点 v 的右子树。
如果 d 的值为 1,深度 d - 1 不存在,则创建一个新的根节点 v,原先的整棵树将作为 v 的左子树。
示例 1:
输入:
二叉树如下所示:
4
/
2 6
/ \ /
3 1 5
v = 1
d = 2
输出:
4
/
1 1
/
2 6
/ \ /
3 1 5
示例 2:
输入:
二叉树如下所示:
4
/
2
/ \
3 1
v = 1
d = 3
输出:
4
/
2
/ \
1 1
/ \
3 1
注意:
输入的深度值 d 的范围是:[1,二叉树最大深度 + 1]。
输入的二叉树至少有一个节点。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/add-one-row-to-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
注意:
此处的遍历必须是保留父节点,将父节点下面填充
递归的话则是判断递归的下面,将上面填充
// 层序遍历
public TreeNode addOneRow1(TreeNode root, int v, int d) {
if(root == null) return null;
// root至少有一个节点
if (d == 1) {
TreeNode node = new TreeNode(v);
node.left = root;
return node;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
List<TreeNode> parentNode = new ArrayList<>();
int depth = 1;
while (!queue.isEmpty()){
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
if(depth == d-1){
parentNode.add(node);
}
}
depth ++;
if(depth == d){
break;
}
}
// 重构二叉树
for (TreeNode parent : parentNode) {
TreeNode left = parent.left;
TreeNode right = parent.right;
TreeNode newNodeLeft = new TreeNode(v);
TreeNode newNodeRight = new TreeNode(v);
newNodeLeft.left = left;
newNodeRight.right = right;
parent.left = newNodeLeft;
parent.right = newNodeRight;
}
return root;
}
// 递归
public TreeNode addOneRow(TreeNode root, int v, int d) {
if(root == null) return null;
if (d == 1) {
TreeNode node = new TreeNode(v);
node.left = root;
return node;
}
return helper(root,v,d,1);
}
private TreeNode helper(TreeNode root, int v, int d, int depth) {
if(root == null) return null;
// 也就是d在depth的下面,此时需要添加节点
if(d == depth + 1){
TreeNode left = root.left;
TreeNode right = root.right;
TreeNode newNodeLeft = new TreeNode(v);
TreeNode newNodeRight = new TreeNode(v);
newNodeLeft.left = left;
newNodeRight.right = right;
root.left = newNodeLeft;
root.right = newNodeRight;
return root;
}
root.left = helper(root.left,v,d,depth+1);
root.right = helper(root.right,v,d,depth+1);
return root;
}
2019-08-29(寻找重复的子树-652-medium)
给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。
两棵树重复是指它们具有相同的结构以及相同的结点值。
示例 1:
1
/ \
2 3
/ / \
4 2 4
/
4
下面是两个重复的子树:
2
/
4
和
4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-duplicate-subtrees
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这题重点在于需要将root中的值序列化成字符串,然后比较是否相等。
// 将节点值序列化成值
public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
Map<String, Integer> map = new HashMap<>();
List<TreeNode> res = new ArrayList<>();
helper(root, res, map);
return res;
}
public String helper(TreeNode root, List<TreeNode> res, Map<String, Integer> map) {
if (root == null) return "";
// 序列化值比较是否相等
String nodeValue = root.val + "," + helper(root.left, res, map) + "," + helper(root.right, res, map);
if(map.get(nodeValue)!= null && map.get(nodeValue) == 1){
res.add(root);
}
map.put(nodeValue,map.getOrDefault(nodeValue,0)+1);
return nodeValue;
}
2019-08-30(最大二叉树-654-medium)
给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
二叉树的根是数组中的最大元素。
左子树是通过数组中最大值左边部分构造出的最大二叉树。
右子树是通过数组中最大值右边部分构造出的最大二叉树。
通过给定的数组构建最大二叉树,并且输出这个树的根节点。
示例 :
输入:[3,2,1,6,0,5]
输出:返回下面这棵树的根节点:
6
/ \
3 5
\ /
2 0
1
提示:
给定的数组的大小在 [1, 1000] 之间。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
public TreeNode constructMaximumBinaryTree(int[] nums) {
if (nums == null || nums.length == 0) return null;
return buildTree(nums, 0, nums.length - 1);
}
// 这里的实现思路也可以只获取下标,不用获取一个数组的数据
private TreeNode buildTree(int[] nums, int l, int h) {
if (l > h) return null;
// 寻找最大值下标与最大值
int[] max = findMax(nums,l,h);
int index = max[0];
int value = max[1];
// 最大值建立根节点
TreeNode root = new TreeNode(value);
// 小的在左边
root.left = buildTree(nums, l, index - 1);
// 大的在右边
root.right = buildTree(nums, index + 1, h);
return root;
}
public int[] findMax(int[] nums,int l, int h) {
int max = Integer.MIN_VALUE;
int index = 0;
for (int i = l; i <= h; i++) {
if (nums[i] > max) {
max = nums[i];
index = i;
}
}
// 下标和最大值
return new int[]{index, max};
}