1. 树
1.1 【简单】二叉树的前序遍历(144)

/*** 树的前序遍历*/public List<Integer> preorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();Stack<TreeNode> stack = new Stack<>();while (root != null || !stack.isEmpty()) {while (root != null) {list.add(root.value);stack.push(root);root = root.left;}if (!stack.isEmpty()) {root = stack.pop();root = root.right;}}return list;}
1.2 【简单】二叉树的中序遍历(94)

/*** 树的中序遍历*/public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();Stack<TreeNode> stack = new Stack<>();while (root != null || !stack.isEmpty()) {while (root != null) {stack.push(root);root = root.left;}if (!stack.isEmpty()) {root = stack.pop();result.add(root.value);root = root.right;}}return result;}
1.3 【简单】二叉树的后序遍历(145)

/*** 树的后序遍历*/public List<Integer> postorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();Stack<TreeNode> stack1 = new Stack<>();Stack<Integer> stack2 = new Stack<>();int left = 1;int right = 2;while (root != null || !stack1.isEmpty()) {while (root != null) {stack1.push(root);stack2.push(left);root = root.left;}while (!stack1.isEmpty() && stack2.peek() == right) {stack2.pop();result.add(stack1.pop().value);}if (!stack1.isEmpty() && stack2.peek() == left) {root = stack1.peek().right;stack2.pop();stack2.push(right);}}return result;}
1.4 【简单】N 叉树的前序遍历(589)

/*** N叉树的前序遍历*/public List<Integer> preorder(Node root) {List<Integer> res = new ArrayList<>();if (root == null) {return res;}Deque<Node> stack = new ArrayDeque<>();stack.push(root);while (!stack.isEmpty()) {Node node = stack.pop();res.add(node.val);for (int i = node.children.size() - 1; i >= 0; --i) {stack.push(node.children.get(i));}}return res;}
1.5 【简单】N 叉树的后序遍历(590)
/*** N叉树的后序遍历,可以利用中右左反转即为左右中(后序)*/public List<Integer> postorder(Node root) {List<Integer> res = new ArrayList<>();if (root == null) {return res;}Deque<Node> stack = new ArrayDeque<>();stack.push(root);while (!stack.isEmpty()) {Node node = stack.pop();res.add(node.val);for (Node item : node.children) {stack.push(item);}}Collections.reverse(res);return res;}
1.6 【中等】N 叉树的层序遍历(429)

/*** 队列即可,与二叉树的层序遍历相似*/public List<List<Integer>> levelOrder(Node root) {List<List<Integer>> ret = new ArrayList<>();if (root == null) {return ret;}Queue<Node> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {List<Integer> level = new ArrayList<>();int currentLevelSize = queue.size();for (int i = 1; i <= currentLevelSize; ++i) {Node node = queue.poll();level.add(node.val);if (node.children != null) {for (Node child : node.children) {queue.offer(child);}}}ret.add(level);}return ret;}
1.7 【中等】从前序与中序遍历序列构造二叉树(105)

递归
/*** 递归构建左右节点,先统计出各自的数量以及确定index*/public class BuildTree {private Map<Integer, Integer> indexMap;public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorderLeft, int preorderRight, int inorderLeft, int inorderRight) {if (preorderLeft > preorderRight) {return null;}// 前序遍历中的第一个节点就是根节点int preorderRoot = preorderLeft;// 在中序遍历中定位根节点int inorderRoot = indexMap.get(preorder[preorderRoot]);// 先把根节点建立出来TreeNode root = new TreeNode(preorder[preorderRoot]);// 得到左子树中的节点数目int sizeLeftSubtree = inorderRoot - inorderLeft;// 递归地构造左子树,并连接到根节点// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素root.left = myBuildTree(preorder, inorder, preorderLeft + 1, preorderLeft + sizeLeftSubtree, inorderLeft, inorderRoot - 1);// 递归地构造右子树,并连接到根节点// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素root.right = myBuildTree(preorder, inorder, preorderLeft + sizeLeftSubtree + 1, preorderRight, inorderRoot + 1, inorderRight);return root;}public TreeNode buildTree(int[] preorder, int[] inorder) {int n = preorder.length;// 构造哈希映射,帮助我们快速定位根节点indexMap = new HashMap<>();for (int i = 0; i < n; i++) {indexMap.put(inorder[i], i);}return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);}}
非递归
/*** 有点难,记下关键步骤*/public TreeNode myBuildTree(int[] preorder, int[] inorder) {if (preorder == null || preorder.length == 0) {return null;}// 前序遍历最左边元素即为根节点TreeNode root = new TreeNode(preorder[0]);Deque<TreeNode> stack = new LinkedList<>();stack.push(root);int inorderIndex = 0;for (int i = 1; i < preorder.length; i++) {int preorderVal = preorder[i];TreeNode node = stack.peek();// 如果相等了就是当前节点下的最左节点if (node.value != inorder[inorderIndex]) {node.left = new TreeNode(preorderVal);stack.push(node.left);} else {// 如果不等就是右节点,否则一直出栈while (!stack.isEmpty() && stack.peek().value == inorder[inorderIndex]) {node = stack.pop();inorderIndex++;}node.right = new TreeNode(preorderVal);stack.push(node.right);}}return root;}
1.8 【中等】根据前序和后序遍历构造二叉树(889)

/*** 我们令左分支有 L 个节点。我们知道左分支的头节点为 pre[1],但它也出现在左分支的后序遍历的最后。*/public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {int N = preorder.length;if (N == 0) {return null;}TreeNode root = new TreeNode(preorder[0]);if (N == 1) {return root;}int L = 0;for (int i = 0; i < N; ++i) {if (postorder[i] == preorder[1]) {L = i + 1;}}root.left = constructFromPrePost(Arrays.copyOfRange(preorder, 1, L + 1),Arrays.copyOfRange(postorder, 0, L));root.right = constructFromPrePost(Arrays.copyOfRange(preorder, L + 1, N),Arrays.copyOfRange(postorder, L, N - 1));return root;}
1.9 【中等】从中序与后序遍历序列构造二叉树(106)

递归
/*** 后序遍历末尾元素即为根节点,在根据中序遍历进行构建左右子树*/public class BuildTree {int postIdx;int[] postorder;int[] inorder;Map<Integer, Integer> idxMap = new HashMap<>();public TreeNode helper(int inLeft, int inRight) {// 如果这里没有节点构造二叉树了,就结束if (inLeft > inRight) {return null;}// 选择 postIdx 位置的元素作为当前子树根节点int rootVal = postorder[postIdx];TreeNode root = new TreeNode(rootVal);// 根据 root 所在位置分成左右两棵子树int index = idxMap.get(rootVal);// 下标减一postIdx--;// 构造右子树root.right = helper(index + 1, inRight);// 构造左子树root.left = helper(inLeft, index - 1);return root;}public TreeNode buildTree(int[] inorder, int[] postorder) {this.postorder = postorder;this.inorder = inorder;// 从后序遍历的最后一个元素开始postIdx = postorder.length - 1;// 建立(元素,下标)键值对的哈希表int idx = 0;for (Integer val : inorder) {idxMap.put(val, idx++);}return helper(0, inorder.length - 1);}}
非递归
/*** 中序遍历反一下即为右中左,后序遍历反一下为中右左,所以用跟1.7的方法即可*/public TreeNode buildTree2(int[] inorder, int[] postorder) {if (postorder == null || postorder.length == 0) {return null;}TreeNode root = new TreeNode(postorder[postorder.length - 1]);Deque<TreeNode> stack = new LinkedList<>();stack.push(root);int inorderIndex = inorder.length - 1;for (int i = postorder.length - 2; i >= 0; i--) {int postorderVal = postorder[i];TreeNode node = stack.peek();if (node.value != inorder[inorderIndex]) {node.right = new TreeNode(postorderVal);stack.push(node.right);} else {while (!stack.isEmpty() && stack.peek().value == inorder[inorderIndex]) {node = stack.pop();inorderIndex--;}node.left = new TreeNode(postorderVal);stack.push(node.left);}}return root;}
1.10 【中等】路径总和 II(113)

深度优先搜索
/*** 深度优先搜索,dfs递归*/public class PathSum {List<List<Integer>> ret = new LinkedList<>();Deque<Integer> path = new LinkedList<>();public List<List<Integer>> pathSum(TreeNode root, int targetSum) {dfs(root, targetSum);return ret;}public void dfs(TreeNode root, int targetSum) {if (root == null) {return;}path.offerLast(root.value);targetSum -= root.value;if (root.left == null && root.right == null && targetSum == 0) {ret.add(new LinkedList<>(path));}dfs(root.left, targetSum);dfs(root.right, targetSum);path.pollLast();}}
广度优先搜索
/*** 广度优先搜索*/public class PathSum {List<List<Integer>> ret = new LinkedList<>();// key:子节点,value:父节点Map<TreeNode, TreeNode> map = new HashMap<>();public List<List<Integer>> pathSum(TreeNode root, int targetSum) {if (root == null) {return ret;}Queue<TreeNode> queueNode = new LinkedList<>();Queue<Integer> queueSum = new LinkedList<>();queueNode.offer(root);queueSum.offer(0);while (!queueNode.isEmpty()) {TreeNode node = queueNode.poll();int rec = queueSum.poll() + node.value;if (node.left == null && node.right == null) {if (rec == targetSum) {// 符合路径,构造路径返回getPath(node);}} else {if (node.left != null) {// 左节点添加map.put(node.left, node);queueNode.offer(node.left);queueSum.offer(rec);}if (node.right != null) {// 右节点添加map.put(node.right, node);queueNode.offer(node.right);queueSum.offer(rec);}}}return ret;}public void getPath(TreeNode node) {List<Integer> temp = new LinkedList<>();while (node != null) {temp.add(node.value);node = map.get(node);}// 倒序节点Collections.reverse(temp);ret.add(new LinkedList<>(temp));}}
1.11 【困难】剑指 Offer II 048. 序列化与反序列化二叉树

/*** 递归遍历即可*/public String serialize(TreeNode root) {return rserialize(root,"");}public TreeNode deserialize(String data) {String[] dataArray = data.split(",");List<String> dataList = new LinkedList<>(Arrays.asList(dataArray));return rdeserialize(dataList);}public String rserialize(TreeNode root, String str) {if (root == null) {str += "None,";} else {str += root.value + ",";str = rserialize(root.left, str);str = rserialize(root.right, str);}return str;}public TreeNode rdeserialize(List<String> dataList) {if ("None".equals(dataList.get(0))) {dataList.remove(0);return null;}TreeNode root = new TreeNode(Integer.valueOf(dataList.get(0)));dataList.remove(0);root.left = rdeserialize(dataList);root.right = rdeserialize(dataList);return root;}
1.12 【中等】二叉树的最近公共祖先

class Solution {private TreeNode ans;public Solution() {this.ans = null;}private boolean dfs(TreeNode root, TreeNode p, TreeNode q) {if (root == null) return false;boolean lson = dfs(root.left, p, q);boolean rson = dfs(root.right, p, q);if ((lson && rson) || ((root.val == p.val || root.val == q.val) && (lson || rson))) {ans = root;}return lson || rson || (root.val == p.val || root.val == q.val);}public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {this.dfs(root, p, q);return this.ans;}}
定义 fx 表示 x 节点的子树中是否包含 p 节点或 q 节点,如果包含为 true,否则为 false
2. 递归
2.1 【简单】爬楼梯(70)

递归
public int climbStairs(int n) {if (n <= 2) {return n;}return climbStairs( n - 1 ) + climbStairs( n - 2 );}
非递归
public int climbStairs(int n) {int p = 0, q = 0, r = 1;for (int i = 1; i <= n; ++i) {p = q;q = r;r = p + q;}return r;}
2.2 【中等】括号生成(22)

递归
/*** 递归*/public class GenerateParenthesis {List<String> res = new ArrayList<>();public List<String> generateParenthesis(int n) {if (n <= 0) {return res;}getParenthesis("", n, n);return res;}private void getParenthesis(String str, int left, int right) {if (left == 0 && right == 0) {res.add(str);return;}if (left == right) {//剩余左右括号数相等,下一个只能用左括号getParenthesis(str + "(", left - 1, right);} else if (left < right) {//剩余左括号小于右括号,下一个可以用左括号也可以用右括号if (left > 0) {getParenthesis(str + "(", left - 1, right);}getParenthesis(str + ")", left, right - 1);}}}
回溯
/*** 回溯*/public class GenerateParenthesis2 {public List<String> generateParenthesis(int n) {List<String> ans = new ArrayList<>();backtrack(ans, new StringBuilder(), 0, 0, n);return ans;}public void backtrack(List<String> ans, StringBuilder cur, int open, int close, int max) {if (cur.length() == max * 2) {ans.add(cur.toString());return;}if (open < max) {cur.append('(');backtrack(ans, cur, open + 1, close, max);// 删除最后一个元素,方便寻找其他可能性cur.deleteCharAt(cur.length() - 1);}if (close < open) {cur.append(')');backtrack(ans, cur, open, close + 1, max);cur.deleteCharAt(cur.length() - 1);}}}
2.3 【简单】汉诺塔问题(面试题 08.06.)

/*** 将 A 上的所有盘子,借助 B,移动到C 上** @param A 原柱子* @param B 辅助柱子* @param C 目标柱子*/public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {movePlate(A.size(), A, B, C);}private void movePlate(int num, List<Integer> original, List<Integer> auxiliary, List<Integer> target) {if (num == 1) {// 只剩一个盘子时,直接移动即可target.add(original.remove(original.size() - 1));return;}// 将 size-1 个盘子,从 original 移动到 auxiliarymovePlate(num - 1, original, target, auxiliary);// 将 第size个盘子,从 original 移动到 targettarget.add(original.remove(original.size() - 1));// 将 size-1 个盘子,从 auxiliary 移动到 targetmovePlate(num - 1, auxiliary, original, target);}
