題意:面試題07. 重建二叉樹
思路:前序遍歷的順序是「根-左-右」,中序遍歷的順序是「左-中-右」。
那麼,對於整棵樹前序遍歷的結果,第一個值r必定是樹的根結點。若是在中序遍歷的結果中找到r的位置index,那麼index左邊的子數組就都是根結點r的左子樹的中序遍歷結果,index右邊的結點都是根結點右子樹的中序遍歷結果。
而且,根據在中序遍歷中找到的左子樹長度,能夠一樣在前序遍歷的r結點以後找到相同長度的子數組,即爲左子樹的前序遍歷結果。其他的部分便是左子樹的前序遍歷結果。
而後根據左、右子樹的前序遍歷和中序遍歷結果再按照上述規則建立二叉樹。java
class Solution { public TreeNode buildTree(int[] preorder, int[] inorder) { return build(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1); } private TreeNode build(int[] pre, int preL, int preR, int[] in, int inL, int inR) { if (preL > preR || inL > inR) { return null; } TreeNode root = new TreeNode(pre[preL]); int rootIndex = -1; for (int i = inL; i <= inR; i ++) { if (in[i] == pre[preL]) { rootIndex = i; break; } } int leftLen = rootIndex - inL; root.left = build(pre, preL+1, preL+leftLen, in, inL, rootIndex-1); root.right = build(pre, preL+leftLen+1, preR, in, rootIndex+1, inR); return root; } }
題意:面試題12. 矩陣中的路徑
思路:典型的dfs搜索+回溯題,每次朝一個方向進行搜索到底,不知足條件時進行回溯。面試
class Solution { int[][] dir = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; public boolean exist(char[][] board, String word) { char[] arr = word.toCharArray(); boolean[][] visited = new boolean[board.length][board[0].length]; for (int i = 0; i < board.length; i ++) { for (int j = 0; j < board[0].length; j ++) { if (searchWord(board, i, j, arr, 0, visited)) { return true; } } } return false; } private boolean searchWord(char[][] board, int i, int j, char[] word, int w, boolean[][] visited) { if (w == word.length) { return true; } if (i >= 0 && i < board.length && j >= 0 && j < board[0].length && board[i][j] == word[w] && !visited[i][j]) { visited[i][j] = true; for (int[] d : dir) { if (searchWord(board, i + d[0], j + d[1], word, w + 1, visited)) { return true; } } visited[i][j] = false; return false; } return false; } }
題意:面試題13. 機器人的運動範圍
思路:搜索,在邊界條件中加入數位之和不大於k便可。其他條件與搜索一致。數組
class Solution { public int movingCount(int m, int n, int k) { int[][] visited = new int[m][n]; return bfs(visited, m, n, 0, 0, k); } int[][] dirs = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; private int bfs(int[][] visited, int m, int n, int i, int j, int k) { Queue<int[]> queue = new LinkedList<>(); queue.add(new int[]{i, j}); visited[i][j] = 1; int count = 0; int[] tmp; while (!queue.isEmpty()) { tmp = queue.poll(); count ++; for (int[] dir : dirs) { int x = tmp[0] + dir[0]; int y = tmp[1] + dir[1]; if (isValid(visited, m, n, x, y, k)) { queue.add(new int[]{x, y}); visited[x][y] = 1; } } } return count; } private boolean isValid(int[][] visited, int m, int n, int x, int y, int k) { boolean isInBound = (x >= 0 && x < m && y >= 0 && y < n && visited[x][y] == 0); if (!isInBound) { return false; } int sum = 0; char[] arr = String.valueOf(x).toCharArray(); for (char c : arr) { sum += (c - '0'); } arr = String.valueOf(y).toCharArray(); for (char c : arr) { sum += (c - '0'); } return sum <= k; } }
題意:面試題26. 樹的子結構
思路:將A中的每個子結點a做爲根結點,判斷B是否與a的結構相同。isSub判斷a與B的結構是否相同,isSubStructure取A中每個結點與B進行比較。app
class Solution { public boolean isSubStructure(TreeNode A, TreeNode B) { return A != null && B != null && (isSub(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B)); } private boolean isSub(TreeNode A, TreeNode B) { if (B == null) { return true; } else if (A == null || A.val != B.val) { return false; } return isSub(A.left, B.left) && isSub(A.right, B.right); } }
題意:面試題27. 二叉樹的鏡像
思路:以遞歸的方式建立。即把原樹的左孩子結點做爲新樹的右孩子結點,原樹的右孩子結點做爲新樹的左孩子結點。less
class Solution { public TreeNode mirrorTree(TreeNode root) { return createMirror(root); } private TreeNode createMirror(TreeNode root) { if (root == null) { return null; } TreeNode newRoot = new TreeNode(root.val); newRoot.left = createMirror(root.right); newRoot.right = createMirror(root.left); return newRoot; } }
題意:面試題28. 對稱的二叉樹
思路:遞歸方式判斷。對於每個結點,必須知足 左孩子的左結點 == 右孩子的右結點 && 左孩子的右結點 == 右孩子的左結點,那麼這棵二叉樹就是對稱的。函數
class Solution { public boolean isSymmetric(TreeNode root) { if (root == null) { return true; } return isSame(root.left, root.right); } private boolean isSame(TreeNode left, TreeNode right) { if (left == null && right == null) { return true; } else if (left == null || right == null) { return false; } else { boolean flag = (left.val == right.val); return flag && isSame(left.left, right.right) && isSame(left.right, right.left); } } }
題意:面試題32 - I. 從上到下打印二叉樹
思路:二叉樹層次遍歷,使用隊列便可。post
class Solution { public int[] levelOrder(TreeNode root) { List<Integer> list = new ArrayList<>(); Queue<TreeNode> queue = new LinkedList<>(); if (root != null) { queue.add(root); } TreeNode tmp; while (!queue.isEmpty()) { tmp = queue.poll(); list.add(tmp.val); if (tmp.left != null) { queue.add(tmp.left); } if (tmp.right != null) { queue.add(tmp.right); } } int[] out = new int[list.size()]; for (int i = 0 ; i < out.length; i ++) { out[i] = list.get(i); } return out; } }
題意:面試題32 - II. 從上到下打印二叉樹 II
思路:二叉樹層次遍歷。有兩種方式,詳見{% post_link "二叉樹的遍歷" %}ui
class Solution { public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> res = new ArrayList<>(); Queue<TreeNode> queue = new LinkedList<>(); if (root != null) { queue.add(root); } TreeNode last = root; TreeNode nLast = root; List<Integer> list = new ArrayList<>(); TreeNode tmp; while (!queue.isEmpty()) { tmp = queue.poll(); list.add(tmp.val); if (tmp.left != null) { queue.add(tmp.left); nLast = tmp.left; } if (tmp.right != null) { queue.add(tmp.right); nLast = tmp.right; } if (tmp == last) { res.add(list); list = new ArrayList<>(); last = nLast; } } return res; } }
題意:面試題32 - III. 從上到下打印二叉樹 III
思路:與32題相似,增長一個標誌位表示當前打印的是奇數行仍是偶數行,奇數行從列表尾部插入元素,偶數行從列表頭部插入元素。this
class Solution { public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> res = new ArrayList<>(); if (root == null) { return res; } Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); TreeNode tmp; int size; boolean flag = true; List<Integer> list; while (!queue.isEmpty()) { size = queue.size(); list = new ArrayList<>(); for (int i = 0; i < size; i ++) { tmp = queue.poll(); if (flag) { list.add(tmp.val); } else { list.add(0, tmp.val); } if (tmp.left != null) { queue.add(tmp.left); } if (tmp.right != null) { queue.add(tmp.right); } } res.add(list); flag = !flag; } return res; } }
題意:面試題34. 二叉樹中和爲某一值的路徑
思路:最經典的dfs+回溯。dfs遍歷過程當中,使用列表記錄從根結點到當前結點的路徑,到達葉子結點時計算是否知足和爲指定值,而後進行回溯。翻譯
class Solution { List<List<Integer>> res = new ArrayList<>(); public List<List<Integer>> pathSum(TreeNode root, int sum) { if (root == null) { return res; } LinkedList<Integer> path = new LinkedList<>(); dfs(root, sum, path); return res; } private void dfs(TreeNode root, int target, LinkedList<Integer> path) { path.add(root.val); if (root.left == null && root.right == null) { if (target == root.val) { res.add(new ArrayList<>(path)); } path.removeLast(); return; } if (root.left != null) { dfs(root.left, target - root.val, path); } if (root.right != null) { dfs(root.right, target - root.val, path); } path.removeLast(); } }
題意:面試題36. 二叉搜索樹與雙向鏈表
思路:二叉搜索樹的前序遍歷結果是一個遞增的序列。能夠利用這個特色,將遍歷到的點依次鏈接起來。
class Solution { public Node treeToDoublyList(Node root) { inOrder(root); if (p != null) { p.right = head; head.left = p; } return head; } Node head; Node p; private void inOrder(Node root) { if (root == null) { return; } inOrder(root.left); if (head == null) { head = root; p = head; } else { p.right = root; root.left = p; p = root; } inOrder(root.right); } }
題意:面試題37. 序列化二叉樹
思路:序列化過程:將各個結點以特殊符號「_」隔開,空結點以「#」代替,以層序遍歷方式遍歷整棵樹。反序列化的過程當中,以"_"分隔序列化字串,仍以層序方式將分隔出的字串恢復爲樹的結點,遇到「#」表示該結點爲空。
public class Codec { // Encodes a tree to a single string. public String serialize(TreeNode root) { StringBuilder sb = new StringBuilder(); Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); TreeNode tmp; while (!queue.isEmpty()) { tmp = queue.poll(); if (tmp == null) { sb.append("#_"); } else { sb.append(tmp.val).append("_"); queue.add(tmp.left); queue.add(tmp.right); } } String res = sb.toString(); return res.substring(0, res.length() - 1); } // Decodes your encoded data to tree. public TreeNode deserialize(String data) { String[] vals = data.split("_"); if ("#".equals(vals[0])) { return null; } TreeNode root = new TreeNode(Integer.parseInt(vals[0])); int index = 1; Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); TreeNode tmp; while (!queue.isEmpty()) { tmp = queue.poll(); if (!"#".equals(tmp)) { if (!vals[index].equals("#")) { tmp.left = new TreeNode(Integer.parseInt(vals[index])); queue.add(tmp.left); } index ++; if (!vals[index].equals("#")) { tmp.right = new TreeNode(Integer.parseInt(vals[index])); queue.add(tmp.right); } index ++; } } return root; } }
題意:面試題46. 把數字翻譯成字符串
思路:每次考慮一個字符和兩個字符(只有當前位置不是最後一個位置,且兩個字符拼接成的整數小於25時才考慮這種狀況),而後遞歸剩下的字符。當到達數字結尾時結束。
class Solution { private int count = 0; public int translateNum(int num) { translateNum(String.valueOf(num).toCharArray(), 0); return count; } private void translateNum(char[] arr, int start) { if (start >= arr.length) { count ++; return; } translateNum(arr, start + 1); if (start < arr.length - 1 && (arr[start] == '1' || (arr[start] == '2' && arr[start + 1] <= '5'))) { translateNum(arr, start + 2); } } }
題意:面試題54. 二叉搜索樹的第k大節點
思路:二叉樹的中序遍歷(左中右)就是一個遞增序列。只須要對遍歷過程進行改造,將遍歷過程修改成右中左就變爲遞減序列了,判斷是否到達第k個點便可。
class Solution { int value = -1; int count = 0; public int kthLargest(TreeNode root, int k) { printTree(root, k); return value; } private void printTree(TreeNode root, int k) { if (root == null || count >= k) { return; } printTree(root.right, k); count ++; if (count == k) { value = root.val; return; } printTree(root.left, k); } }
題意:面試題55 - I. 二叉樹的深度
思路:遞歸。深度爲左子樹和右子樹最大高度加1獲得。
class Solution { public int maxDepth(TreeNode root) { if (root == null) { return 0; } return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; } }
題意:面試題55 - II. 平衡二叉樹
思路:遞歸。在判斷左子樹和右子樹是否爲平衡二叉樹的同時,也要判斷其高度差是否小於等於1(其中高度的計算能夠由面試題55-I獲得),因此遞歸函數的返回值有兩個:高度和當前結點爲根的子樹是否爲平衡二叉樹。
class Solution { public boolean isBalanced(TreeNode root) { return isBalancedTree(root).isBalanced; } class Returntype { int height; boolean isBalanced; public Returntype(int height, boolean isBalanced) { this.height = height; this.isBalanced = isBalanced; } } private Returntype isBalancedTree(TreeNode root) { if (root == null) { return new Returntype(0, true); } Returntype left = isBalancedTree(root.left); if (!left.isBalanced) { return new Returntype(-1, false); } Returntype right = isBalancedTree(root.right); if (!right.isBalanced) { return new Returntype(-1, false); } if (Math.abs(left.height - right.height) <= 1) { return new Returntype(Math.max(left.height, right.height) + 1, true); } return new Returntype(-1, false); } }
題意:面試題68 - I. 二叉搜索樹的最近公共祖先
思路:根據二叉搜索樹的特色。根結點的值大於左子樹的結點,且小於右子樹上全部的結點。
1)若是根結點的值介於兩個要找的值之間,那麼根結點就是要求的最近公共祖先;
2)若是根結點的值小於兩個要找的值,那麼公共祖先應位於根結點的右子樹上;
3)若是根結點的值大於兩個要找的值,那麼公共祖先應位於根結點的左子樹上;
class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { TreeNode less = p.val > q.val ? q : p; TreeNode large = less == p ? q : p; return commonNode(root, less, large); } private TreeNode commonNode(TreeNode root, TreeNode p, TreeNode q) { if (root == p || root == q) { return root; } else if (root.val > p.val && root.val < q.val) { return root; } else if (root.val < p.val && root.val < q.val) { return commonNode(root.right, p, q); } else { return commonNode(root.left, p, q); } } }
題意:面試題68 - II. 二叉樹的最近公共祖先
思路:遞歸。從根結點開始查找,要求的兩個結點的公共祖先必定是:左子樹上某個結點、右子樹上某個結點或本結點。
class Solution { 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); if (left == null) { return right; } else if (right == null) { return left; } return root; } }