leetcode树相关题目
目录
树的基本操作包括前序、中序及后序的递归及迭代遍历(dfs)、树的层次遍历(bfs)一定要非常非常的熟练!因为树的题目基本都是在建立在这些操作之上的
树的相关题目会频繁地出现递归,递归的一个非常重要的点就是:不去管函数的内部细节是如何处理的,只看其函数作用以及输入与输出
假设树的节点结构为
class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
树的前序、中序及后序遍历的递归版本比较简单
private List<Integer> res = new ArrayList<>();
public List<Integer> preOrder(TreeNode root) {
if (root != null) {
res.add(root.val);
preOrder(root.left);
preOrder(root.right);
}
return res;
}
1. 树的前序遍历(迭代)
见leetcode144
前序遍历就是按照中左右的顺序遍历树
public List<Integer> preOrder(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) return res;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode cur = stack.pop();
res.add(cur.val);
if (cur.right != null) {
stack.push(cur.right);
}
if (cur.left != null) {
stack.push(cur.left);
}
}
return res;
}
2. 树的中序遍历
2.1 树的迭代中序遍历
见leetcode94
中序遍历就是按照左中右的顺序遍历树
public List<Integer> inOrder(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) return res;
Stack<TreeNode> stack = new Stack<>();
while (!stack.isEmpty() || root != null) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
res.add(root.val);
root = root.right;
}
return res;
}
2.2 变形题:验证二叉搜索树 TODO
private long pre = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
// 访问左子树
if (!isValidBST(root.left)) return false;
// 访问当前节点:如果当前节点小于等于中序遍历的前一个节点,说明不满足BST,返回false
if (root.val <= pre) return false;
pre = root.val;
// 访问右子树
return isValidBST(root.right);
}
2.3 变形题:二叉搜索树中第K小的元素
public int kthSmallest(TreeNode root, int k) {
Stack<TreeNode> stack = new Stack<>();
while (!stack.isEmpty() || root != null) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
if (--k == 0) return root.val;
root = root.right;
}
return -1;
}
2.4 变形题:二叉搜索树迭代器
private Queue<Integer> queue = new LinkedList<>();
public BSTIterator(TreeNode root) {
inOrder(root);
}
private void inOrder(TreeNode root) {
if (root != null) {
inOrder(root.left);
queue.offer(root.val);
inOrder(root.right);
}
}
public int next() {
return queue.poll();
}
public boolean hasNext() {
return !queue.isEmpty();
}
3. 树的后序遍历(迭代)
见leetcode145
后序遍历就是按照左右中的顺序遍历树
public List<Integer> postOrder(TreeNode root) {
// 注意要用LinkedList才行,List接口没有addFirst方法
LinkedList<Integer> res = new LinkedList<>();
if (root == null) return res;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode cur = stack.pop();
res.addFirst(cur.val);
if (cur.left != null) {
stack.push(cur.left);
}
if (cur.right != null) {
stack.push(cur.right);
}
}
return res;
}
4. 树的层次遍历
4.1 迭代版本
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int cur = 1;
int next = 0;
List<Integer> in = new ArrayList<>();
while (!queue.isEmpty()) {
TreeNode t = queue.poll();
cur--;
in.add(t.val);
if (t.left != null) {
queue.offer(t.left);
next++;
}
if (t.right != null) {
queue.offer(t.right);
next++;
}
if (cur == 0) {
res.add(new ArrayList<>(in));
in.clear();
cur = next;
next = 0;
}
}
return res;
}
4.2 递归版本
private List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
dfs(root, 0, res);
return res;
}
private void dfs(TreeNode root, int level, List<List<Integer>> res) {
if (root == null) return;
if (level >= res.size()) res.add(new ArrayList<>());
res.get(level).add(root.val);
dfs(root.left, level + 1, res);
dfs(root.right, level + 1, res);
}
4.3 变形题:树的锯齿形层次遍历
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int cur = 1;
int next = 0;
boolean flag = false;
List<Integer> in = new ArrayList<>();
while (!queue.isEmpty()) {
TreeNode t = queue.poll();
in.add(t.val);
cur--;
if (t.left != null) {
queue.offer(t.left);
next++;
}
if (t.right != null) {
queue.offer(t.right);
next++;
}
if (cur == 0) {
if (flag) Collections.reverse(in);
res.add(in);
flag = !flag;
in = new ArrayList<>();
cur = next;
next = 0;
}
}
return res;
}
4.4 变形题:树的右视图
public List<Integer> rightSideView(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) return res;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int cur = 1;
int next = 0;
while (!queue.isEmpty()) {
TreeNode t = queue.poll();
if (--cur == 0) {
res.add(t.val);
}
if (t.left != null) {
queue.offer(t.left);
next++;
}
if (t.right != null) {
queue.offer(t.right);
next++;
}
if (cur == 0) {
cur = next;
next = 0;
}
}
return res;
}
5. 重建二叉树
5.1 前序中序遍历构造二叉树
public TreeNode buildTree(int[] preorder, int[] inorder) {
return buildTree(preorder, 0, inorder, inorder.length - 1, 0);
}
private TreeNode buildTree(int[] preorder, int idx, int[] inorder, int end, int start) {
if (idx >= preorder.length || start > end) return null;
int i = 0;
for (i = end; i >= start; i--) {
if (preorder[idx] == inorder[i]) break;
}
TreeNode root = new TreeNode(preorder[idx]);
root.left = buildTree(preorder, idx + 1, inorder, i - 1, start);
root.right = buildTree(preorder, idx + i - start + 1, inorder, end, i + 1);
return root;
}
5.2 后序中序遍历构造二叉树
public TreeNode buildTree(int[] inorder, int[] postorder) {
return buildTree(postorder, postorder.length - 1, inorder, inorder.length - 1, 0);
}
private TreeNode buildTree(int[] postorder, int idx, int[] inorder, int end, int start) {
if (idx < 0 || start > end) return null;
int i;
for (i = end; i >= start; i--) {
if (inorder[i] == postorder[idx]) break;
}
TreeNode root = new TreeNode(postorder[idx]);
root.left = buildTree(postorder, idx - end + i - 1, inorder, i - 1, start);
root.right = buildTree(postorder, idx - 1, inorder, end, i + 1);
return root;
}
6. 树的公共祖先
6.1 二叉搜索树的最近公共祖先
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
if (root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);
return root;
}
6.2 二叉树的最近公共祖先
6.2.1 递归版
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
return left == null ? right : right == null ? left : root;
}
6.2.2 迭代版
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
Map<TreeNode, TreeNode> parent = new HashMap<>();
Deque<TreeNode> stack = new ArrayDeque<>();
parent.put(root, null);
stack.push(root);
while (!parent.containsKey(p) || !parent.containsKey(q)) {
TreeNode node = stack.pop();
if (node.left != null) {
parent.put(node.left, node);
stack.push(node.left);
}
if (node.right != null) {
parent.put(node.right, node);
stack.push(node.right);
}
}
Set<TreeNode> ancestors = new HashSet<>();
while (p != null) {
ancestors.add(p);
p = parent.get(p);
}
while (!ancestors.contains(q)) q = parent.get(q);
return q;
}
7. 树的深度
7.1 树的最大深度
public int maxDepth(TreeNode root) {
if (root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
7.2 树的最小深度 TODO
public int minDepth(TreeNode root) {
if (root == null) return 0;
int m1 = minDepth(root.left);
int m2 = minDepth(root.right);
return root.left == null || root.right == null ? m1 + m2 + 1 : Math.min(m1, m2) + 1;
}
7.3 变形题:二叉树的直径
private int res = 0;
public int diameterOfBinaryTree(TreeNode root) {
if (root == null) return 0;
depth(root);
return res;
}
private int depth(TreeNode root) {
if (root == null) return 0;
int left = depth(root.left);
int right = depth(root.right);
res = Math.max(res, left + right);
return Math.max(left, right) + 1;
}
7.4 变形题:二叉树中的最大路径和 TODO
private int res = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
findMax(root);
return res;
}
private int findMax(TreeNode root) {
if (root == null) return 0;
int left = Math.max(0, findMax(root.left));
int right = Math.max(0, findMax(root.right));
res = Math.max(res, left + right + root.val);
return Math.max(left, right) + root.val;
}
8. 树的对称、翻转、平衡、合并
8.1 对称二叉树
public boolean isSymmetric(TreeNode root) {
return isMirror(root, root);
}
private boolean isMirror(TreeNode t1, TreeNode t2) {
if (t1 == null && t2 == null) return true;
if (t1 == null || t2 == null) return false;
return t1.val == t2.val && isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left);
}
8.2 翻转二叉树
8.2.1 递归版
public TreeNode invertTree(TreeNode root) {
if (root == null) return root;
TreeNode left = invertTree(root.left);
TreeNode right = invertTree(root.right);
root.left = right;
root.right = left;
return root;
}
8.2.2 迭代版
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode cur = stack.pop();
TreeNode tmp = cur.right;
cur.right = cur.left;
cur.left = tmp;
if (cur.left != null) stack.push(cur.left);
if (cur.right != null) stack.push(cur.right);
}
return root;
}
8.2.3 递归版(不破坏原来的树)
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;
TreeNode newNode = new TreeNode(root.val);
newNode.left = invertTree(root.right);
newNode.right = invertTree(root.left);
return newNode;
}
8.2.4 迭代版(不破坏原来的树)
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
Stack<TreeNode> newStack = new Stack<>();
TreeNode newRoot = new TreeNode(root.val);
newStack.push(newRoot);
while (!stack.isEmpty()) {
TreeNode cur = stack.pop();
TreeNode newCur = newStack.pop();
if (cur.right != null) {
stack.push(cur.right);
newCur.left = new TreeNode(cur.right.val);
newStack.push(newCur.left);
}
if (cur.left != null) {
stack.push(cur.left);
newCur.right = new TreeNode(cur.left.val);
newStack.push(newCur.right);
}
}
return newRoot;
}
8.3 平衡二叉树
public boolean isBalanced(TreeNode root) {
if (root == null) return true;
int left = depth(root.left);
int right = depth(root.right);
return Math.abs(left - right) <= 1 && isBalanced(root.left) && isBalanced(root.right);
}
private int depth(TreeNode root) {
if (root == null) return 0;
return Math.max(depth(root.left), depth(root.right)) + 1;
}
8.4 合并二叉树
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if (t1 == null) return t2;
if (t2 == null) return t1;
TreeNode root = new TreeNode(t1.val + t2.val);
root.left = mergeTrees(t1.left, t2.left);
root.right = mergeTrees(t1.right, t2.right);
return root;
}
9. 二叉树转链表
9.1 二叉树展开为链表
public void flatten(TreeNode root) {
if (root == null) return;
flatten(root.left);
flatten(root.right);
TreeNode tmp = root.right;
root.right = root.left;
root.left = null;
while (root.right != null) root = root.right;
root.right = tmp;
}
9.2 二叉搜索树展开为双向链表 TODO
见牛客网
9.2.1 迭代版
public TreeNode Convert(TreeNode root) {
if (root == null) return null;
TreeNode head = null, cur = root, pre = null;
Stack<TreeNode> stack = new Stack<>();
while (!stack.isEmpty() || cur != null) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
if (!stack.isEmpty()) {
cur = stack.pop();
if (head == null) head = cur;
if (pre != null) {
pre.right = cur;
cur.left = pre;
}
pre = cur;
cur = cur.right;
}
}
return head;
}
9.2.2 递归版
public TreeNode Convert(TreeNode root) {
root = convertBST2DLL(root);
while (root.left != null) root = root.left;
return root;
}
private TreeNode convertBST2DLL(TreeNode root) {
if (root == null || (root.left == null && root.right == null)) return root;
TreeNode cur;
if (root.left != null) {
cur = convertBST2DLL(root.left);
while (cur.right != null) cur = cur.right;
cur.right = root;
root.left = cur;
}
if (root.right != null) {
cur = convertBST2DLL(root.right);
while (cur.left != null) cur = cur.left;
cur.left = root;
root.right = cur;
}
return root;
}
10. 二叉树的序列化与反序列化
public String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
if (root == null) {
sb.append("#,");
return sb.toString();
}
sb.append(root.val + ",");
sb.append(serialize(root.left));
sb.append(serialize(root.right));
return sb.toString();
}
public TreeNode deserialize(String data) {
String[] arr = data.split(",");
Queue<String> queue = new LinkedList<>();
for (String str : arr) queue.offer(str);
return deserialize(queue);
}
private TreeNode deserialize(Queue<String> queue) {
String str = queue.poll();
if (str.equals("#")) return null;
TreeNode root = new TreeNode(Integer.parseInt(str));
root.left = deserialize(queue);
root.right = deserialize(queue);
return root;
}
11. 有序数组转换为二叉搜索树
public TreeNode sortedArrayToBST(int[] nums) {
if (nums == null || nums.length == 0) return null;
return buildTree(nums, 0, nums.length - 1);
}
private TreeNode buildTree(int[] nums, int lo, int hi) {
if (lo > hi) return null;
int mid = (lo + hi) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = buildTree(nums, lo, mid - 1);
root.right = buildTree(nums, mid + 1, hi);
return root;
}
12. 删除二叉搜索树中的节点
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return null;
if (key < root.val) {
root.left = deleteNode(root.left, key);
} else if (key > root.val) {
root.right = deleteNode(root.right, key);
} else {
if (root.left == null) {
return root.right;
} else if (root.right == null) {
return root.left;
} else {
TreeNode node = root.right;
while (node.left != null) node = node.left;
node.left = root.left;
return root.right;
}
}
return root;
}
13. 树的第K层节点个数(递归)
主要是理解递归的思想
public static int getNodeNumKthLevel(TreeNode root, int k) {
if (root == null || k < 1) return 0;
if (k == 1) return 1;
return getNodeNumKthLevel(root.left, k - 1) + getNodeNumKthLevel(root.right, k - 1);
}
14. 树的叶子节点个数(递归)
public static int getNodeNumLeaf(TreeNode root) {
if (root == null) return 0;
if (root.left == null && root.right == null) return 1;
return getNodeNumLeaf(root.left) + getNodeNumLeaf(root.right);
}
15. 判断树是否为完全二叉树(迭代)
public static boolean isCompleteBinaryTree(TreeNode root) {
if (root == null) return false;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
boolean mustHaveNoChild = false;
boolean result = true;
while (!queue.isEmpty()) {
TreeNode cur = queue.remove();
if (mustHaveNoChild) {
if (cur.left != null || cur.right != null) {
result = false;
break;
}
} else {
if (cur.left != null && cur.right != null) {
queue.add(cur.left);
queue.add(cur.right);
} else if (cur.left != null) {
mustHaveNoChild = true;
queue.add(cur.left);
} else if (cur.right != null) {
result = false;
break;
} else {
mustHaveNoChild = true;
}
}
}
return result;
}