---------------------------------------------------------------
本文使用方法:所有題目,只需要把標題輸入lintcode就能找到。主要是簡單的剖析思路以及不能bug-free的具體細節原因。
----------------------------------------------------------------
-------------------------------------------
第九周:圖和搜索。
-------------------------------------------
1,-------Clone Graph(克隆圖)
(1)答案和思路:首先用list來記錄有哪些圖,然后對於每一個節點都要新建。用map來做新舊節點的一一對應,以便於后面的建立鄰結點。

/** * Definition for undirected graph. * class UndirectedGraphNode { * int label; * ArrayList<UndirectedGraphNode> neighbors; * UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); } * }; */ public class Solution { /** * @param node: A undirected graph node * @return: A undirected graph node */ public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) { if (node == null) { return null; } ArrayList<UndirectedGraphNode> nodes = new ArrayList(); nodes.add(node); Map<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap(); map.put(node, new UndirectedGraphNode(node.label)); int index = 0; while (index < nodes.size()) { UndirectedGraphNode curNode = nodes.get(index++); for (int i = 0; i < curNode.neighbors.size(); i++) { if (!nodes.contains(curNode.neighbors.get(i))) { nodes.add(curNode.neighbors.get(i)); map.put(curNode.neighbors.get(i), new UndirectedGraphNode(curNode.neighbors.get(i).label)); } } } for (int i = 0; i < nodes.size(); i++) { UndirectedGraphNode curNode = nodes.get(i); for (int j = 0; j < curNode.neighbors.size(); j++) { map.get(curNode).neighbors.add(map.get(curNode.neighbors.get(j))); } } return map.get(node); } }
(2)一刷沒有AC:思路沒有處理好。
(2)二刷bug-free;
2,-----------Toplogical Sorting(拓撲排序)
(1)答案和思路:首先,利用map來記錄入度。並且把沒進去map的那些點先加入結果,因為他們入度為0.為了方便遍歷,用queue來輔助遍歷。

/** * Definition for Directed graph. * class DirectedGraphNode { * int label; * ArrayList<DirectedGraphNode> neighbors; * DirectedGraphNode(int x) { label = x; neighbors = new ArrayList<DirectedGraphNode>(); } * }; */ public class Solution { /** * @param graph: A list of Directed graph node * @return: Any topological order for the given graph. */ public ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) { ArrayList<DirectedGraphNode> result = new ArrayList(); if (graph == null || graph.size() == 0) { return result; } HashMap<DirectedGraphNode, Integer> map = new HashMap(); //map 用來記錄那些成為了別人鄰居的點作為多少個鄰居,也就是他的入度是多少。 for (DirectedGraphNode g : graph) { for (DirectedGraphNode n : g.neighbors) { if (map.containsKey(n)) { map.put(n, map.get(n) + 1); } else { map.put(n, 1); } } } Queue<DirectedGraphNode> queue = new LinkedList(); for (DirectedGraphNode g : graph) { if (!map.containsKey(g)) { //不在map里面,說明他的度為0 result.add(g); queue.add(g); } } while (!queue.isEmpty()) { DirectedGraphNode node = queue.poll(); for (DirectedGraphNode n : node.neighbors) { map.put(n, map.get(n) - 1); if (map.get(n) == 0) { result.add(n); queue.add(n); } } } return result; } }
(2)一刷沒過。
3,------permutation(全排列)
(1)答案和思路:就是沒一個數字加進去之前,他可以放在已經有的地方的n+1個位置。不斷地循環就可以了。

class Solution { /** * @param nums: A list of integers. * @return: A list of permutations. */ public static ArrayList<ArrayList<Integer>> permute(ArrayList<Integer> nums) { ArrayList<ArrayList<Integer>> res = new ArrayList(); if (nums == null || nums.size() == 0) { return res; } ArrayList<Integer> list1 = new ArrayList(); list1.add(nums.get(0)); res.add(list1); for (int i = 1; i < nums.size(); i++){ ArrayList<ArrayList<Integer>> res2 = new ArrayList(); for (ArrayList<Integer> tmp : res) { for (int j = 0; j <= tmp.size(); j++) { ArrayList<Integer> list2 = new ArrayList(tmp); list2.add(j, nums.get(i)); res2.add(list2); } } res = new ArrayList(res2); } return res; } }
(2)注意細節問題。
(3)二刷bug-free;
4,---------permutationII(全排列)
(1)答案和思路:跟上一題的區別就只需要再加入結果之前判斷一下是否已經有了。

class Solution { /** * @param nums: A list of integers. * @return: A list of unique permutations. */ public static ArrayList<ArrayList<Integer>> permuteUnique(ArrayList<Integer> nums) { // write your code here HashSet<ArrayList<Integer>> res = new HashSet(); ArrayList<ArrayList<Integer>> ans = new ArrayList(); if (nums == null || nums.size() == 0) { return ans; } ArrayList<Integer> list1 = new ArrayList(); list1.add(nums.get(0)); res.add(list1); for (int i = 1; i < nums.size(); i++){ HashSet<ArrayList<Integer>> res2 = new HashSet(); for (ArrayList<Integer> tmp : res) { for (int j = 0; j <= tmp.size(); j++) { ArrayList<Integer> list2 = new ArrayList(tmp); list2.add(j, nums.get(i)); res2.add(list2); } } res = new HashSet(res2); } for (ArrayList list : res){ ArrayList<Integer> list3 = new ArrayList(list); ans.add(list3); } return ans; } }
(2)一刷bug-free;
5,----------N Queens (n皇后問題)
(1)答案和思路:首先,就是一場不斷嘗試放置的過程,那么需要一個函數來判斷是否能夠放(不能放的條件:這一列已經放了,他不能和上面的行構成斜對角線(當行差值==列差值的時候,就是在對角線上))。技巧:columns[i] = j來記錄每一行的Q放在哪里。i行放在j列。 第二步就是進行放置:放置對於第一行來說,能放N個位置,所以,for循環。放好了第一個,利用遞歸,開始放第二。不斷的判斷是否可行,直到放置行數達到N。那么就是一次合法的放置,存進list里面。

import java.util.ArrayList; public class Solution { public static void main(String[] args) { System.out.println(solveNQueens(4)); } public static ArrayList<ArrayList<String>> solveNQueens(int n) { ArrayList<ArrayList<String>> result = new ArrayList(); int[] columns = new int[n]; for (int i = 0; i < n; i++) { columns[i] = -1; } ArrayList<int[]> results = new ArrayList(); placeNQueens(0, columns, results, n); for (int[] r : results) { ArrayList<String> list = new ArrayList(); for (int i = 0; i < r.length; i++) { char[] c = new char[n]; for (int j = 0; j < n; j++) { c[j] = '.'; } c[r[i]] = 'Q'; list.add(new String(c)); } result.add(new ArrayList(list)); } return result; } private static void placeNQueens(int row, int[] columns, ArrayList<int[]> results, int n) { if (row == n) { results.add(columns.clone()); } else { for (int col = 0; col < n; col++) { if (checkValid(columns, row, col)) { columns[row] = col; placeNQueens(row + 1, columns, results, n); } } } } // columns[i] = j 表示第i行第j列放皇后 private static boolean checkValid(int[] columns, int row, int column) { // 看一下跟已有的行的哪些列沖突了 // i 到 row 行之間這些行已經存了東西了。columns[i]能得到存了哪些列 for (int i = 0; i < row; i++) { int hasColumn = columns[i]; if (hasColumn == column) { return false; } if (row - i == Math.abs(column - hasColumn)) { // 如果行之間的差值等於列之間的差值,那么在同一個對角線 return false; } } return true; } }
(2)一刷沒過。完全沒想到怎么判斷。
6,--------Palindrome Partition I(回文區分)
(1)答案和思路:首先,拿到0到第i個字符,如果他是回文,那么存下來,然后從他到結尾,這一個字符串,也來進行0,到1,。。,i的判斷。
基本思路就是從0-i是否是回文,是的話,接着處理i到結束的字符串遞歸:在后面的字符串里繼續考慮:第一個到新的i是否是回文。如果一直這樣處理,直到最后的str為空了,那么說明找到一組完整的了,因為只有是回文才會進入遞歸。這里就是遞歸結束之后,要remove掉list的最后一個,這是因為:假如,0-i是,但是后來一直處理結束了都不是,那么這就不是一組合格的,那么遞歸結束,這個值必須出局。想法如果全部都是,那么一層一層往外走,也需要list一個一個刪除。

import java.util.ArrayList; import java.util.List; public class Solution { public static void main(String[] args) { System.out.println(partition("aaa")); } public static List<List<String>> partition(String s) { List<List<String>> result = new ArrayList(); if (null == s || s.length() == 0) { return result; } List<String> list = new ArrayList(); calResult(result, list, s); return result; } public static void calResult(List<List<String>> result, List<String> list, String str) { if (str == null || str.length() == 0) { // ERROR : result.add(list); result.add(new ArrayList(list)); } for (int i = 1; i <= str.length(); i++) { String subStr = str.substring(0, i); if (isPalindrome(subStr)) { list.add(subStr); calResult(result, list, str.substring(i)); list.remove(list.size() - 1); } } } public static boolean isPalindrome(String s) { if (null == s || s.length() == 0) { return false; } int i = 0; int j = s.length() - 1; while (i < j) { if (s.charAt(i) != s.charAt(j)) { return false; } i++; j--; } return true; } }
(2)一刷沒過。
(3)二刷錯在:list存入是需要new的。不然一直是原來的list,是錯的。
7,----------combination sum(求一個集合里面能夠組合成目標值的組合種類)
(1)答案和思路:這里依然是利用遞歸來做。不同的就是,從i開始,在sum沒有大於target之前,再次遞歸的元素下標是不變的。

import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Solution { public static void main(String[] args) { int[] a = {7, 1, 2, 5, 1, 6, 10}; System.out.println(combinationSum(a, 8)); } public static List<List<Integer>> combinationSum(int[] candidates, int target) { List<List<Integer>> result = new ArrayList(); if (candidates == null || candidates.length == 0) { return result; } List<Integer> list = new ArrayList(); Arrays.sort(candidates); calResult(candidates,target, 0, 0, result, list); return result; } public static void calResult(int[] candidates, int target, int sum, int index, List<List<Integer>> result, List<Integer> list) { if (sum > target) { return; } if (sum == target) { if (!result.contains(list)) { result.add(new ArrayList(list)); } } for (int i = index; i < candidates.length; i++) { sum += candidates[i]; list.add(candidates[i]); calResult(candidates, target, sum, i, result, list); sum -= candidates[i]; list.remove(list.size() - 1); } } }
(2)一刷思路沒弄對。
(3)二刷:忘記了sort數組。因為結果要求有序。
8, --------------combination sum II(和I的區別是,每個元素只能用一次,那么就是每一次遞歸都是i+1的走)
答案:

import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Solution { // public static void main(String[] args) { // int[] a = {7, 1, 2, 5, 1, 6, 10}; // System.out.println(combinationSum(a, 8)); // } public static List<List<Integer>> combinationSum2(int[] candidates, int target) { List<List<Integer>> result = new ArrayList(); if (candidates == null || candidates.length == 0) { return result; } List<Integer> list = new ArrayList(); Arrays.sort(candidates); calResult(candidates,target, 0, 0, result, list); return result; } public static void calResult(int[] candidates, int target, int sum, int index, List<List<Integer>> result, List<Integer> list) { if (sum > target) { return; } if (sum == target) { if (!result.contains(list)) { result.add(new ArrayList(list)); } } for (int i = index; i < candidates.length; i++) { sum += candidates[i]; list.add(candidates[i]); calResult(candidates, target, sum, i + 1, result, list); sum -= candidates[i]; list.remove(list.size() - 1); } } }
(1)一刷bug-free;
9,--------------word ladder
(1)答案和思路:利用隊列來進行記錄每一次可以有多少個next直到遇到了end。

import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; import java.util.Set; public class Solution { public static int ladderLength(String start, String end, Set<String> dict) { if (dict == null) { return 0; } dict.add(start); dict.add(end); HashSet<String> hash = new HashSet(); Queue<String> queue = new LinkedList(); queue.add(start); hash.add(start); int length = 1; while (!queue.isEmpty()) { length++; int size = queue.size(); for (int i = 0; i < size; i++) { String word = queue.poll(); dict.remove(word); for (String nextWord : getNextWords(word, dict)) { if (hash.contains(nextWord)) { continue; } if (nextWord.equals(end)) { return length; } hash.add(nextWord); queue.add(nextWord); } } } return 0; } private static String replace(String s, int index, char c) { char[] chars = s.toCharArray(); chars[index] = c; return new String(chars); } private static ArrayList<String> getNextWords(String word, Set<String> dict) { ArrayList<String> nextWords = new ArrayList(); for (char c = 'a'; c <= 'z'; c++) { for (int i = 0; i < word.length(); i++) { if (c == word.charAt(i)) { continue; } String nextWord = replace(word, i, c); if (dict.contains(nextWord)) { nextWords.add(nextWord); } } } return nextWords; } }
(2)注意如何找next。要利用好字符范圍,通過改變字符的方式來找。因為dict太大。不能。利用contain。O(1)復雜度。
。
。
。
。
。
。
8,------------
-------------------------------------------
第八周:數據結構。
-------------------------------------------
1,------------min stack(最小棧)
答案:注意錯誤點,不要只判斷null,還要判斷size()

public class MinStack { Stack<Integer> s1 = new Stack(); Stack<Integer> s2 = new Stack(); public MinStack() { // do initialize if necessary } public void push(int number) { // write your code here s1.push(number); if (s2 == null || s2.size() == 0) { s2.push(number); } else { if (number < s2.peek()) { s2.push(number); } else { s2.push(s2.peek()); } } } public int pop() { // write your code here s2.pop(); return s1.pop(); } public int min() { // write your code her return s2.peek(); } }
(1)一刷所犯錯誤是:沒判斷size==0;
(2)二刷bug-free;
2,---------implement a queue by two stacks(用兩個棧實現一個隊列)
答案:1,這里有一個很好的結構就是,在class里面做出一定的定義,然后new的時候在結構函數里面。

public class Queue { private Stack<Integer> stack1; private Stack<Integer> stack2; public Queue() { stack1 = new Stack(); stack2 = new Stack(); // do initialization if necessary } public void push(int element) { // write your code here stack1.push(element); } public int pop() { // write your code here if (stack2.isEmpty()) { while (!stack1.isEmpty()) { stack2.push(stack1.pop()); } } return stack2.pop(); } public int top() { // write your code here if (stack2.isEmpty()) { while (!stack1.isEmpty()) { stack2.push(stack1.pop()); } } return stack2.peek(); } }
(1)一刷忘記了在構造函數里面初始化。
(2)2刷bug-free;
3,----------largest rectangle in histogram(在直方圖中最大的矩形)(再一次驗證了lintcode的數據集不嚴謹,卡時間不嚴謹,暴力也能過。這道題公認的暴力是過不了的。)
答案和思路:利用stack來存index。只要是遞增的時候,就一直往里放,如果不是遞增,那么就往外pop。

public class Solution { public int largestRectangleArea(int[] heights) { if (null == heights || heights.length == 0) { return 0; } int area = 0; Stack<Integer> stack = new Stack(); for (int i = 0; i < heights.length; i++) { if (stack.empty() || heights[stack.peek()] < heights[i]) { stack.push(i); } else { int start = stack.pop(); int width = stack.isEmpty() ? i : i - stack.peek() - 1; area = Math.max(area, heights[start] * width); i--; } } while (!stack.isEmpty()) { int start = stack.pop(); int width = stack.isEmpty() ? heights.length : heights.length - stack.peek() - 1; area = Math.max(area, heights[start] * width); } return area; } }
(1)下次重點看着道題。
4,-----------
5,-----------rehashing(重哈希)
答案:

/** * Definition for ListNode * public class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { /** * @param hashTable: A list of The first node of linked list * @return: A list of The first node of linked list which have twice size */ public ListNode[] rehashing(ListNode[] hashTable) { // write your code here int capacity = hashTable.length; ListNode[] res = new ListNode[2 * capacity]; for (int i = 0; i < capacity; i++) { ListNode a = hashTable[i]; while (a != null) { int index = 0; if (a.val >= 0) { index = a.val % (2 * capacity); } else { index = (a.val % (2 * capacity) + (2 * capacity)) % (2 * capacity); } if (res[index] == null) { res[index] = new ListNode(a.val); } else { ListNode tmp = res[index]; while (tmp.next != null) { tmp = tmp.next; } tmp.next = new ListNode(a.val); } a = a.next; } } return res; } }
6,-----------
7,-----------median number(中位數)
答案:

public static int[] medianII(int[] nums) { if (nums == null || nums.length == 0) { return null; } int len = nums.length; int[] res = new int[len]; ArrayList<Integer> list = new ArrayList(); for (int i = 0; i < len; i++) { list.add(nums[i]); Collections.sort(list); res[i] = list.get((list.size() - 1) / 2); } return res; }
8,---------ugly number(丑數)
答案:

public static long kthPrimeNumber(int k) { Queue<Long> q1 = new PriorityQueue(); Queue<Long> q2 = new PriorityQueue(); Queue<Long> q3 = new PriorityQueue(); q1.offer((long) 3); q2.offer((long) 5); q3.offer((long) 7); long res = 0; for (int i = 0; i < k; i++) { if (q1.peek() < q2.peek()) { if (q1.peek() < q3.peek()) { res = q1.poll(); if (!q1.contains(res * 3)) { q1.offer(res * 3); } if (!q1.contains(res * 5)) { q1.offer(res * 5); } if (!q1.contains(res * 7)) { q1.offer(res * 7); } } else { res = q3.poll(); if (!q3.contains(res * 7)) { q3.offer(res * 7); } } } else { if (q2.peek() < q3.peek()) { res = q2.poll(); if (!q2.contains(res * 5)) { q2.offer(res * 5); } if (!q2.contains(res * 7)) { q2.offer(res * 7); } } else { res = q3.poll(); if (!q3.contains(res * 7)) { q3.offer(res * 7); } } } } return res; }
9,-------merge K sorted arrays(合並k個有序的數組)
答案:

public static List<Integer> mergekSortedArrays(int[][] arrays) { List<Integer> res = new ArrayList(); int height = arrays.length; int[] index = new int[height]; boolean flag = true; while (flag) { flag = false; int min = Integer.MAX_VALUE; int minIndex = 0; for (int i = 0; i < height; i++) { if (arrays[i] != null && index[i] < arrays[i].length && arrays[i][index[i]] < min) { minIndex = i; min = arrays[i][index[i]]; flag = true; } } if (!flag) { break; } res.add(min); index[minIndex]++; } return res; }
10,-----------
-------------------------------------------
第七周:數組。
-------------------------------------------
1,---------------merge sorted array I(歸並兩個有序的數組)
答案:注意1,要判斷有一個是否已經copy完了。其次要注意是A還是B。

public void mergeSortedArray(int[] A, int m, int[] B, int n) { // write your code here // error :b < 0 int a = m - 1; int b = n - 1; for (int i = m + n - 1; i >= 0; i--) { if (b < 0) { A[i] = A[a--]; } else if (a < 0) { A[i] = A[b--]; } else if (A[a] > B[b]) { A[i] = A[a--]; } else { A[i] = B[b--]; } } }
(2)二刷bug-free;
2,------------merge sorted array II (歸並兩個有序的數組)
答案:

class Solution { /** * @param A and B: sorted integer array A and B. * @return: A new sorted integer array */ public int[] mergeSortedArray(int[] A, int[] B) { // Write your code here int lenA = A.length; int lenB = B.length; int len = lenA + lenB; int[] result = new int[len]; int indexA = lenA - 1; int indexB = lenB - 1; int index = len - 1; while (index >= 0) { if (indexA >= 0 && (indexB < 0 || (A[indexA] >= B[indexB]))) { result[index--] = A[indexA--]; } else { result[index--] = B[indexB--]; } } return result; } }
(2)二刷bug-free;
3,------------median of two sorted Arrays(找兩個有序數組的中位數)
答案:需要改進,這是暴力解法。沒有logn

public double findMedianSortedArrays(int[] a, int[] b) { // write your code here int n = a.length + b.length; int[] tmp = mergeSortedArray(a, b); if (n % 2 == 0) { return (tmp[n / 2 - 1] + tmp[n / 2]) / 2.0; } else { return (double) tmp[n / 2]; } } public static int[] mergeSortedArray(int[] a, int[] b) { // Write your code here int m = a.length; int n = b.length; int[] result = new int[m + n]; int len1 = 0; int len2 = 0; for (int i = 0; i < m + n; i++) { if (len1 >= m) { result[i] = b[len2++]; } else if (len2 >= n) { result[i] = a[len1++]; } else if (a[len1] < b[len2]) { result[i] = a[len1++]; } else { result[i] = b[len2++]; } } return result; }
4,-------best time to buy and sell stock I(買賣股票最佳時機)
答案:

public int maxProfit(int[] prices) { // write your code here int max = 0; int[] dp = new int[prices.length]; for (int i = 0; i < prices.length; i++) { for (int j = i - 1; j >= 0; j--) { if (prices[j] < prices[i]) { dp[i] = Math.max(dp[i], prices[i] - prices[j]); } max = Math.max(max, dp[i]); } } return max; }
(2)一刷bug-free;
II
答案:

public int maxProfit(int[] prices) { // write your code here int maxProfit = 0; for (int i = 1; i < prices.length; i++) { if (prices[i] - prices[i - 1] > 0) { maxProfit += prices[i] - prices[i - 1]; } } return maxProfit; }
(2)bug-free;
III,
答案:思路:就是通過left,right來判斷,就是到了這個點,前面最大能有多少利益,后面又有多少。(再一次顯示了lintcode的敷衍性,數據太少了,時間卡的不及時。如果單純的left,right來不斷算的話會超時。所以需要用數組來進行操作)

public class Solution { public int maxProfit(int[] prices) { if (null == prices || prices.length == 0) { return 0; } int max = 0; int min = prices[0]; int[] left = new int[prices.length]; int[] right = new int[prices.length]; for (int i = 1; i < prices.length; i++) { min = Math.min(min, prices[i]); left[i] = Math.max(left[i - 1], prices[i] - min); } max = prices[prices.length - 1]; for (int i = prices.length - 2; i >= 0; i--) { max = Math.max(max, prices[i]); right[i] = Math.max(right[i + 1], max - prices[i]); } System.out.println(Arrays.toString(left)); int profit = 0; for (int i = 0; i < prices.length; i++) { profit = Math.max(profit, left[i] + right[i]); } return profit; } }
(1)一刷錯誤是:邊界沒有考慮清楚,比如說left【】是從第一個開始算,所以min要初始化為第0個。
(2)bug-free;
IV,
答案和思路:
5,---maximum subarray(最大子數組)
(I)答案:

public int maxSubArray(int[] nums) { // write your code if (null == nums || nums.length == 0) { return 0; } int len = nums.length; int[] dp = new int[len]; dp[0] = nums[0]; int max = nums[0]; for (int i = 1; i < len; i++) { if (dp[i - 1] > 0) { dp[i] = dp[i - 1] + nums[i]; } else { dp[i] = nums[i]; } max = Math.max(max, dp[i]); } return max; }
bug-free;
-------------------------------------------
第六周:鏈表。
-------------------------------------------
1,---------remove duplicates from sorted list II(從有序的鏈表中移除重復元素)
(1)答案和思路:注意的地方就是記得判斷null。

public class Solution { /** * @param ListNode head is the head of the linked list * @return: ListNode head of the linked list */ public static ListNode deleteDuplicates(ListNode head) { // write your code here if (null == head) { return head; } ListNode preHead = new ListNode(-1); preHead.next = head; ListNode pre = preHead; ListNode cur = head; while (cur != null && cur.next != null) { if (cur.val != cur.next.val) { cur = cur.next; pre = pre.next; continue; } while (cur.next != null && cur.val == cur.next.val) { cur = cur.next; } pre.next = cur.next; cur = cur.next; } return preHead.next; } }
(2)一刷bug-free;
2,-----------reverse linked list II(翻轉鏈表)
(1)
翻轉鏈表1:就是建一個preHead,每次來都插進去。用temp記一下preHead.next;注意的地方是要先把head = head.next。不然一會head.next改變了

public class Solution { /** * @param head: The head of linked list. * @return: The new head of reversed linked list. */ public ListNode reverse(ListNode head) { // write your code here if (null == head || head.next == null) { return head; } ListNode preHead = new ListNode(0); while (head != null) { ListNode temp = preHead.next; preHead.next = head; head = head.next; preHead.next.next = temp; } return preHead.next; } }
翻轉鏈表II:注意不要用next做判斷,因為它是會變的。
(3)二刷bug-free;
3,-------partition list(分割鏈表)
(1)答案:主要是思路是開兩個list來操作

public static ListNode partition(ListNode head, int x) { if (null == head) { return head; } ListNode preHead = new ListNode(0); preHead.next = head; ListNode left = preHead; ListNode right = preHead; while (left.next != null) { if (left.next.val < x) { left = left.next; continue; } right = left; while (right.next != null && right.next.val >= x) { right = right.next; } if (right.next == null) { break; } ListNode tmp = right.next; right.next = right.next.next; ListNode tmp2 = left.next; left.next = tmp; tmp.next = tmp2; left = left.next; } head = preHead.next; return head; }
(2)一刷不能bug-free:是因為思路。
(3)二刷bug-free;
4,--------------sort list
(1)答案和思路:因為要求O(nlogn) ,所以用歸並來做。

/** * Definition for ListNode. * public class ListNode { * int val; * ListNode next; * ListNode(int val) { * this.val = val; * this.next = null; * } * } */ public class Solution { /** * @param head: The head of linked list. * @return: You should return the head of the sorted linked list, using constant space complexity. */ public ListNode sortList(ListNode head) { // write your code here if (head == null || head.next == null) { return head; } ListNode mid = findMiddle(head); ListNode right = sortList(mid.next); mid.next = null; ListNode left = sortList(head); return merge(left, right); } private ListNode findMiddle(ListNode head) { ListNode slow = head; ListNode fast = head.next; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; } return slow; } public static ListNode merge(ListNode head1, ListNode head2) { if (head1 == null) { return head2; } if (head2 == null) { return head1; } ListNode preHead = new ListNode(0); ListNode cur = preHead; while (head1 != null || head2 != null) { if (head1 != null && (head2 == null || head1.val < head2.val)) { cur.next = new ListNode(head1.val); head1 = head1.next; cur = cur.next; } else { cur.next = new ListNode(head2.val); head2 = head2.next; cur = cur.next; } } return preHead.next; } }
(2)一刷AC需要三次:1,歸並沒有記熟系。2,找中點的時候fast=head.next;
(3)二刷:bug-free;
5,--------reorder list(重排鏈表)
(1)答案和思路:因為要本地in-place。所以,對后半部分進行reverse,然后接進去。

public class Solution { /** * @param head: The head of linked list. * @return: void */ public void reorderList(ListNode head) { // write your code here if (head == null || head.next == null) { return; } ListNode middle = findMiddle(head); ListNode newMiddle = reverseList(middle.next); middle.next = null; while (newMiddle != null) { ListNode temp = head.next; head.next = newMiddle; newMiddle = newMiddle.next; head.next.next = temp; head = head.next.next; } } public static ListNode findMiddle(ListNode head) { if (head == null || head.next == null) { return head; } ListNode slow = head; ListNode fast = head.next; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } return slow; } public static ListNode reverseList(ListNode head) { if (head == null || head.next == null) { return head; } ListNode preHead = new ListNode(0); while (head != null) { ListNode temp = preHead.next; preHead.next = head; head = head.next; preHead.next.next = temp; } return preHead.next; } }
(2)一刷AC需要兩次:錯在fast.next.next誤寫成fast.next:
(3)二刷AC: 第二次所犯錯誤是reverse函數中返回preHead是錯的。
6,-------------鏈表有無環判斷(Linked list cycle)
答案:bug-free;

public boolean hasCycle(ListNode head) { // write your code here ListNode fast = head; ListNode slow = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) { return true; } } return false; }
7,------------rotate list(右移動鏈表)
(1)答案和思路:注意,如果k % length == 0 要特判斷。

public ListNode rotateRight(ListNode head, int k) { // write your code here if (head == null ) { return head; } ListNode preHead = new ListNode(0); preHead.next = head; int length = 0; ListNode pre = preHead; while (pre.next != null) { length++; pre = pre.next; } int n = k % length; if (n == 0) { return head; } for (int i = 0; i < length - n; i++) { ListNode tmp = preHead.next; preHead.next = preHead.next.next; pre.next = tmp; tmp.next = null; pre = pre.next; } head = preHead.next; return head; }
(2)一刷沒法AC是因為:k % length == 0 沒有判斷導致后面出現了空指針。
(3)二刷bug-free;
8,--------------Merge k sorted list(歸並k個有序鏈表)
答案: bug-free;但是下次要看一下九章的其他算法。

public ListNode mergeKLists(List<ListNode> lists) { // write your code here // error1 : input [] if (lists == null || lists.size() == 0) { return null; } int min = Integer.MAX_VALUE; int flag = -1; boolean b = true; ListNode pre = new ListNode(0); ListNode cur = pre; while (b) { b = false; int i = 0; for (ListNode list : lists) { if (list != null && list.val < min) { flag = i; min = list.val; b = true; } i++; } if (!b) { break; } cur.next = new ListNode(min); lists.set(flag, lists.get(flag).next); cur = cur.next; min = Integer.MAX_VALUE; } return pre.next; }
9,------------copy list with random pointer(復制帶有隨機指針的鏈表)
(1)答案:在原表上進行操作

public static RandomListNode copyRandomList(RandomListNode head) { // write your code here RandomListNode cur = head; while (cur != null) { RandomListNode tmp = new RandomListNode(cur.label); RandomListNode next = cur.next; cur.next = tmp; tmp.next = next; cur = cur.next.next; } cur = head; while (cur != null) { if (cur.random == null) { cur.next.random = null; } else { cur.next.random = cur.random.next; } cur = cur.next.next; } RandomListNode head2 = head.next; RandomListNode cur2 = head2; while (cur2.next != null) { cur2.next = cur2.next.next; cur2 = cur2.next; } return head2; }
(2)一刷AC不了是因為:有next.next的時候沒有注意判斷.next可以搞定不。而且注意題目里面是label和RandomListNode。
(3)二刷: bug-free;
14,--------Remove Nth Node From End of List(刪除鏈表中倒數第n個值)
(1)答案:

ListNode removeNthFromEnd(ListNode head, int n) { // write your code here int length = 0; ListNode preHead = new ListNode(0); preHead.next = head; ListNode cur = head; while (cur != null) { length++; cur = cur.next; } ListNode pre = preHead; for (int i = 0; i < length - n; i++) { pre = pre.next; } pre.next = pre.next.next; head = preHead.next; return head; }
(2)注意的地方是:如果刪除的頭結點。所以要建一個新的頭結點。
15,--------------Linked List cycle(找鏈表循環的起點)
(1)答案:

public ListNode detectCycle(ListNode head) { // write your code here ListNode fast = head; ListNode slow = head; boolean isHave = false; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; if (fast == slow) { isHave = true; break; } } if (!isHave) { return null; } slow = head; while (slow != fast) { slow = slow.next; fast = fast.next; } return fast; }
(2)一次沒有AC是因為沒環要返回null,沒有注意到。
-------------------------------------------
第五周:動態規划二
-------------------------------------------
1,------------ 分割回文串 II(Palindrome Partitioning II)
(1)答案:第i個字符串的分割dp[i] = 他到前面某一個是回文然后加上前面那一段所需要的最少分割 +1。

public class Solution { /** * @param s a string * @return an integer */ public int minCut(String s) { // write your code here if (null == s || s.length() == 0) { return 0; } int length = s.length(); int[] dp = new int[length + 1]; for (int i = 0; i <= length; i++) { dp[i] = i - 1; } for (int i = 1 ; i <= length; i++) { for (int j = i; j > 0; j--) { if (isPalindrome(s, j - 1, i - 1)) { dp[i] = Math.min(dp[i], dp[j - 1] + 1); } } } return dp[length]; } public static boolean isPalindrome(String s, int start, int end) { if (null == s || s.length() == 0) { return true; } while (start < end) { // if (!s.get(start).equals(s.get(end))) { if (s.charAt(start) != s.charAt(end)) { return false; } start++; end--; } return true; } }
(2)一刷沒有AC。
(3)二刷:bug-free;
2,----------------Word Break(單詞切分)
(1)答案:注意要考慮單詞的長度

public class Solution { /** * @param s: A string s * @param dict: A dictionary of words dict */ public boolean wordBreak(String s, Set<String> dict) { // write your code here if (s == null || s.length() == 0) { return true; } if (dict == null) { return false; } int length = s.length(); int maxWordLength = 0; for (String str : dict) { maxWordLength = Math.max(maxWordLength, str.length()); } boolean[] dp = new boolean[length + 1]; dp[0] = true; for (int i = 1; i <= length; i++) { for (int j = i; j > 0 && i - j <= maxWordLength; j--) { if (dp[j - 1] && dict.contains(s.substring(j - 1, i))) { dp[i] = true; break; } } } return dp[length]; } }
(2)一刷LTE了,原因是沒有考慮單詞的長度。
(3)二刷沒有bug-free,是因為j--錯寫成j++.
3,---------Longest Common Subsequence(最長公共子序列)
注意:個數是從0個開始。
(1)答案和思路:1,就是取第一個字符串前i個,第二個字符串前j個寫遞推式。

public int longestCommonSubsequence(String A, String B) { // write your code here // error1 : 忘記判斷空了 if (A == null || A.length() == 0 || B == null || B.length() == 0) { return 0; } int[][] dp = new int[A.length() + 1][B.length() + 1]; // error3:int[][] dp = new int[A.length()][B.length()]; for (int i = 1; i <= A.length(); i++) { for (int j = 1; j <= B.length(); j++) { if (A.charAt(i - 1) == B.charAt(j - 1)) {// error4 :if (A.charAt(i) == B.charAt(j)) { dp[i][j] = Math.max(Math.max(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1] + 1); } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } return dp[A.length()][B.length()]; }
(2)一刷沒有AC。
(3)二刷bug-free;
5,------------Edit Distance(編輯距離)
(1)答案:

public int minDistance(String word1, String word2) { // write your code here // error1: 忘記判斷空了 if ((word1 == null || word1.length() == 0) && (word2 == null || word2.length() == 0)) { return 0; } int m = word1.length(); int n = word2.length(); int[][] dp = new int[m + 1][n + 1]; for (int i = 0; i < m + 1; i++) { dp[i][0] = i; } for (int j = 0; j < n + 1; j++) { dp[0][j] = j; } for (int i = 1; i < m + 1; i++) { for (int j = 1; j < n + 1; j++) { if (word1.charAt(i - 1) == word2.charAt(j - 1)) { dp[i][j] = Math.min(Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1]); } else { dp[i][j] = Math.min(Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + 1); } } } return dp[m][n]; }
(2)bug-free;
6,----------distinct subsequences(不同的子序列)
(1)答案和思路: 前i,前j思路。

public int numDistinct(String s, String t) { // write your code here if (t == null) { return 1; } if (s == null) { return 0; } int m = s.length(); int n = t.length(); int[][] dp = new int[m + 1][n + 1]; for (int i = 0; i < m + 1; i++) { dp[i][0] = 1; } for (int i = 1; i < m + 1; i++) { for (int j = 1; j < n + 1; j++) { if (s.charAt(i - 1) == t.charAt(j - 1)) { dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1]; } else { dp[i][j] = dp[i - 1][j]; } } } return dp[m][n]; }
(2)沒有bug-free,因為錯寫了charAt(i)應該是i - 1;
(3)二刷bug-free;
7,---------Interleaving String(交叉字符串)
(1)答案和思路:也就是前i,j思路

public class Solution { /** * Determine whether s3 is formed by interleaving of s1 and s2. * @param s1, s2, s3: As description. * @return: true or false. */ public boolean isInterleave(String s1, String s2, String s3) { // write your code here if (s1 == null || s2 == null || s3 == null) { return false; } int len1 = s1.length(); int len2 = s2.length(); int len3 = s3.length(); if (len1 + len2 != len3) { return false; } boolean[][] dp = new boolean[len1 + 1][len2 + 1]; dp[0][0] = true; for (int i = 1; i < len1 + 1; i++) { if (s1.charAt(i - 1) == s3.charAt(i - 1)) { dp[i][0] = true; } else { break; } } for (int j = 1; j < len2 + 1; j++) { if (s2.charAt(j - 1) == s3.charAt(j - 1)) { dp[0][j] = true; } else { break; } } for (int i = 1; i < len1 + 1; i++) { for (int j = 1; j < len2 + 1; j++) { dp[i][j] = dp[i - 1][j] && s1.charAt(i - 1) == s3.charAt(i + j - 1) || dp[i][j - 1] && s2.charAt(j - 1) == s3.charAt(i + j - 1); } } return dp[len1][len2]; } }
(2)一刷沒有做對。
(3)二刷bug-free;
-------------------------------------------
第四周:動態規划一(動態規划三要素:1,求最大最小值;2,判斷是否可行;3,統計方案個數;)
不使用動態規划:求具體方案個數,不是序列而是集合。因為dp要求順序不能變。
-------------------------------------------
1,-----------Triangle( 數字三角形)
(1)答案:

public static int minimumTotal(int[][] triangle) { int[][] res = new int[triangle.length][triangle[triangle.length - 1].length]; for (int j = 0; j < triangle[triangle.length - 1].length; j++) { res[triangle.length - 1][j] = triangle[triangle.length - 1][j]; } for (int i = triangle.length - 2; i >= 0; i--) { for (int j = 0; j < triangle[i].length; j++) { res[i][j] = triangle[i][j] + Math.min(res[i + 1][j], res[i + 1][j + 1]); } } return res[0][0]; }
(2)一刷沒有做對是因為注意dp過程中到底是加dp的值還是triangle的值。此外dp比較麻煩可以考慮從后往前。
(3)二刷所犯錯誤:i--誤寫成i++;
3,-------------Longest Consecutive Sequence( 最長連續序列)
答案:(1)這是自己寫的,已經先排序了。要找更優化的答案:

public static int longestConsecutive(int[] num) { int length = 1; Arrays.sort(num); int max = 1; for (int i = 1; i < num.length; i++) { if (num[i] - num[i - 1] == 1) { max++; } else if (num[i] - num[i -1] == 0) { continue; } else { length = Math.max(length, max); max = 1; } } return Math.max(length, max); }
(2)
4,------------Minimum Path Sum(最小路徑和)
(1)答案:注意length千萬別再寫錯了。此外注意千萬別忘記了加上這一格的值。

public int minPathSum(int[][] grid) { // write your code here int[][] dp = new int[grid.length][grid[0].length]; dp[0][0] = grid[0][0]; for (int j = 1; j < grid[0].length; j++) { dp[0][j] = dp[0][j - 1] + grid[0][j]; } int sum2 = dp[0][0]; for (int i = 1; i < grid.length; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; } for (int i = 1; i < grid.length; i++) { for (int j = 1; j < grid[0].length; j++) { dp[i][j] = grid[i][j] + Math.min(dp[i - 1][j], dp[i][j - 1]); } } return dp[grid.length - 1][grid[0].length - 1]; }
(2)bug-free;
5,-----------Unique Paths(不同的路徑)
(1)答案:一定要注意i,j位置。bug=free;

public int uniquePaths(int m, int n) { // write your code here int[][] dp = new int[m][n]; for (int i = 0; i < m; i++) { dp[i][0] = 1; } for (int j = 0; j < n; j++) { dp[0][j] = 1; } for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; } } return dp[m - 1][n - 1]; }
(2)一刷bug-free;
6,------------Climbing Stairs(爬樓梯)
(1)答案:不要忘記特判斷。

public int climbStairs(int n) { // write your code here if (n <= 1) { return 1; } int[] dp = new int[n]; dp[0] = 1; dp[1] = 2; for (int i = 2; i < n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n - 1]; }
(2)一刷:3次才能AC。所犯錯誤是:首先,台階是0的時候應該返回0; 其次是注意下標i從2開始遞推。
(3)二刷:bug-free;
7,---------------Jump Game(跳躍游戲)
(1)答案:注意單詞的拼寫。length;

public boolean canJump(int[] A) { // wirte your code here boolean[] result = new boolean[A.length]; result[0] = true; for (int i = 0; i < A.length; i++) { if (!result[i]){ return false; } for (int j = i + 1; j < A.length && j < i + 1 + A[i]; j++) { result[j] = true; } } return result[A.length - 1]; }
(2)bug-free;
8,-------------Jump Game II(跳躍游戲 II)
(1)答案:思路是,dp[0] = 0;然后從1開始,找他前面能到他的min+1;能到等價於:a[j] >= i - j;

public int jump(int[] A) { // write your code here int[] dp = new int[A.length]; dp[0] = 0; for (int i = 1; i < A.length; i++) { int min = Integer.MAX_VALUE; for (int j = 0; j < i; j++) { if (A[j] >= i - j) { min = Math.min(min, dp[j]); } } dp[i] = min + 1; } return dp[A.length - 1]; }
(2)bug-free;
9,------------Unique Paths II(不同的路徑 II)
(1)答案:

public int uniquePathsWithObstacles(int[][] obstacleGrid) { // write your code here int m = obstacleGrid.length; int n = obstacleGrid[0].length; int[][] dp = new int[m][n]; for (int i = 0; i < m; i++) { if (obstacleGrid[i][0] != 1) { dp[i][0] = 1; } else { break; } } for (int j = 0; j < n; j++) { if (obstacleGrid[0][j] != 1) { dp[0][j] = 1; } else { break; } } for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { if (obstacleGrid[i][j] != 1) { dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; } } } return dp[m - 1][n - 1]; }
(2)一刷AC需要2次,所犯錯誤是:在初始化第一列/行的時候不僅僅是有障礙物的設為0而是他以后的都設置為0,因為不可達到了。
(3)二刷bug-free;
10,---------------------Longest Increasing Subsequence(最長上升子序列)
答案:

public int longestIncreasingSubsequence(int[] nums) { // write your code here // error1: 忘記判斷空了 // error2: 1 1 1 1 1 1 1 if (null == nums || nums.length == 0) { return 0; } int res = 0; int[] dp = new int[nums.length]; for (int i = 0; i < dp.length; i++) { dp[i] = 1; for (int j = i - 1; j >= 0; j--) { if (nums[j] <= nums[i]) { dp[i] = Math.max(dp[i], dp[j] + 1); } } res = Math.max(res, dp[i]); } return res; }
-------------------------------------------
第三周:二叉樹和分治法
-------------------------------------------
碰到二叉樹的問題的時候,就想想整棵樹在該問題上的結果和左右兒子在該問題上的結果之間的聯系是什么。
1,------------------Binary Tree Preorder Traversal(二叉樹的前序遍歷)
(1)用遞歸:bug-free;

import java.util.ArrayList; public class Solution { /** * @param root: The root of binary tree. * @return: Preorder in ArrayList which contains node values. */ public ArrayList<Integer> preorderTraversal(TreeNode root) { // write your code here ArrayList<Integer> preorder = new ArrayList(); preorderTraversal(root, preorder); return preorder; } public static void preorderTraversal(TreeNode root, ArrayList<Integer> preorder) { if (null == root) { return; } preorder.add(root.val); preorderTraversal(root.left, preorder); preorderTraversal(root.right, preorder); } }
(2)非遞歸(必考):
2,--------------------Binary Tree Inorder Traversal(二叉樹的中序遍歷)
(1)用遞歸:bug-free;

public ArrayList<Integer> inorderTraversal(TreeNode root) { // write your code here ArrayList<Integer> res = new ArrayList(); inorderTraversal(root, res); return res; } public static void inorderTraversal(TreeNode root, ArrayList<Integer> list) { if (root == null) { return; } inorderTraversal(root.left, list); list.add(root.val); inorderTraversal(root.right, list); }
(2)非遞歸(必考):
3,---------------Binary Tree Postorder Traversal(二叉樹的后序遍歷)
(1)用遞歸:bug-free;

public ArrayList<Integer> postorderTraversal(TreeNode root) { // write your code here ArrayList<Integer> res = new ArrayList(); postorderTraversal(root, res); return res; } public static void postorderTraversal(TreeNode root, ArrayList<Integer> list) { if (root == null) { return; } postorderTraversal(root.left, list); postorderTraversal(root.right, list); list.add(root.val); }
(2)非遞歸(必考):
4,--------------------Maximum Depth of Binary Tree(二叉樹的最大深度)
答案:bug-free;

public int maxDepth(TreeNode root) { // write your code here if (root == null) { return 0; } return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; }
5,--------------Balanced Binary Tree(平衡二叉樹)
(1)答案:

public class Solution { /** * @param root: The root of binary tree. * @return: True if this Binary tree is Balanced, or false. */ public boolean isBalanced(TreeNode root) { // write your code here if (root == null) { return true; } return (Math.abs(height(root.right) - height(root.left)) <= 1) && isBalanced(root.left) && isBalanced(root.right); } public static int height(TreeNode root) { if (null == root) { return 0; } return Math.max(height(root.left), height(root.right)) + 1; } }
(2)一刷需要2次才能AC。主要是錯在,一些書寫錯誤,少了函數名之類的。
(2)二刷:bug-free;
6,----------------Lowest Common Ancestor(最近公共祖先)
(1)答案:思路在代碼中注釋了

public class Solution { /** * @param root: The root of the binary search tree. * @param A and B: two nodes in a Binary. * @return: Return the least common ancestor(LCA) of the two nodes. */ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode a, TreeNode b) { // write your code here if (root == null || root == a || root == b) { return root; } TreeNode left = lowestCommonAncestor(root.left, a, b); TreeNode right = lowestCommonAncestor(root.right, a, b); if (left != null && right != null) { return root; } else if (left != null) { return left; } else { return right; } } }
(2)一刷需要三次才能AC: 首先是沒搞懂怎么弄。 其次是||與&&的錯用。
(3)二刷:bug-free;
7,----------------Binary Tree Maximum Path Sum(二叉樹中的最大路徑和)
(1)思路:思路:這里的最大路勁和,其實可以這么理解,1,過root的;2,不過root的。這里又分為兩種,就是過left的,過right的,然后遞歸就可以了。可以認為是這條路徑總是要從某一個結點開始往下走的

public class Solution { /** * @param root: The root of binary tree. * @return: An integer. */ static int max = Integer.MIN_VALUE; public int maxPathSum(TreeNode root) { // write your code here // 思路:這里的最大路勁和,其實可以這么理解,1,過root的;2,不過root的。這里又分為兩種,就是過left的,過right的,然后遞歸就可以了。可以認為是這條路徑總是要從某一個結點開始往下走的。 if (null == root) { return 0; } path(root); return max; } public static int path(TreeNode root) { if (root == null) { return 0; } int leftPath = path(root.left); int rightPath = path(root.right); int curMax = root.val + Math.max(0, Math.max(leftPath, rightPath)); max = Math.max(max, Math.max(curMax, root.val + leftPath + rightPath)); return curMax; } }
(2)一刷沒做對。
(2)Binary Tree Maximum Path Sum II( 二叉樹中的最大路徑和)
第二種情況,這里比較easy,因為要求過root:
(1)答案:注意遇到了樹的題目,首先考慮他和他的左右子樹之間有什么聯系。

public int maxPathSum2(TreeNode root) { // Write your code here if (null == root) { return 0; } int leftSum = maxPathSum2(root.left); int rightSum = maxPathSum2(root.right); return root.val + Math.max(0, Math.max(leftSum, rightSum)); }
(2)一刷沒做對;
8,------------------Binary Tree Level Order Traversal(二叉樹的層次遍歷)
(1)答案:注意,其實每一層就是當前queue還有的都是上一層的,所以用好queue.size來做循環。此外注意queue的add是offer。pop是poll。打好基礎知識。

public class Solution { /** * @param root: The root of binary tree. * @return: Level order a list of lists of integer */ public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) { // write your code here ArrayList<ArrayList<Integer>> levelOrder = new ArrayList(); if (null == root) { return levelOrder; } Queue<TreeNode> queue = new LinkedList(); queue.add(root); while (!queue.isEmpty()) { ArrayList<Integer> list = new ArrayList(); int size = queue.size(); for (int i = 0; i < size; i++) { TreeNode top = queue.poll(); if (top.left != null) { queue.add(top.left); } if (top.right != null) { queue.add(top.right); } list.add(top.val); } levelOrder.add(list); } return levelOrder; } }
(2) 一刷需要兩次AC:注意一下,queue.size()是一直在改變的,所以,循環條件不能用它,而應該是先記下來上一層的size。
(3)二刷bug-free;
9,--------------------Binary Tree Level Order Traversal II(二叉樹的層次遍歷 II)
(1) 答案:注意,和上一題的差別就在於利用了list的add插入。

import java.util.ArrayList; import java.util.LinkedList; public class Solution { /** * @param root: The root of binary tree. * @return: buttom-up level order a list of lists of integer */ public ArrayList<ArrayList<Integer>> levelOrderBottom(TreeNode root) { // write your code here ArrayList<ArrayList<Integer>> levelOrder = new ArrayList(); if (null == root) { return levelOrder; } Queue<TreeNode> queue = new LinkedList(); queue.add(root); while (!queue.isEmpty()) { ArrayList<Integer> list = new ArrayList(); int size = queue.size(); for (int i = 0; i < size; i++) { TreeNode top = queue.poll(); list.add(top.val); if (top.left != null) { queue.add(top.left); } if (top.right != null) { queue.add(top.right); } } levelOrder.add(0, list); } return levelOrder; } }
(2) 一刷的時候bug-free;
10,---------------Binary Tree Zigzag Level Order Traversal(二叉樹的鋸齒形層次遍歷)
答案。需要逆序的那一層就利用add(位置,值)。然后用flag來控制是否逆過來。

import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; public class Solution { /** * @param root: The root of binary tree. * @return: A list of lists of integer include * the zigzag level order traversal of its nodes' values */ public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) { // write your code here ArrayList<ArrayList<Integer>> traversal = new ArrayList(); if (root == null) { return traversal; } Queue<TreeNode> queue = new LinkedList(); int level = 0; queue.add(root); while (!queue.isEmpty()) { ArrayList<Integer> list = new ArrayList(); int size = queue.size(); for (int i = 0; i < size; i++) { TreeNode top = queue.poll(); if (top.left != null) { queue.add(top.left); } if (top.right != null) { queue.add(top.right); } if (level % 2 == 0) { list.add(top.val); } else { list.add(0, top.val); } } traversal.add(list); level++; } return traversal; } }
(2)一刷的時候bug-free;
11,---------------------(驗證二叉查找樹)
答案:注意,int不夠了。

public boolean isValidBST(TreeNode root) { // write your code here return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE); } public static boolean isValidBST(TreeNode root, long left, long right) { if (root == null) { return true; } return (root.val > left && root.val < right) && isValidBST(root.left, left, root.val) && isValidBST(root.right, root.val, right); }
12,-------- Inorder Successor in Binary Search Tree(中序后繼)
(1)答案:這是search tree;利用好search tree的性質。

public class Solution { public TreeNode inorderSuccessor(TreeNode root, TreeNode p) { // write your code here if (root == null) { return null; } TreeNode successor = null; while (root != null) { if (root != p && root.val > p.val) { successor = root; root = root.left; } else { root = root.right; } } return successor; } }
(2)一定要仔細閱讀題目,這是search tree;利用好search tree的性質。
(3)二刷:bug-free;
13,---------------------Binary Search Tree Iterator( 二叉查找樹迭代器)
答案:

public class BSTIterator { //@param root: The root of binary tree. ArrayList<TreeNode> list = new ArrayList<TreeNode>(); int flag = 0; public BSTIterator(TreeNode root) { // write your code here list = inorderTraversal(root); } //@return: True if there has next node, or false public boolean hasNext() { // write your code here if (list == null || flag >= list.size()) { return false; } return true; } //@return: return next node public TreeNode next() { // write your code here if (list == null || flag >= list.size()) { return null; } return list.get(flag++); } public static ArrayList<TreeNode> inorderTraversal(TreeNode root) { // write your code here ArrayList<TreeNode> res = new ArrayList(); inorderTraversal(root, res); return res; } public static void inorderTraversal(TreeNode root, ArrayList<TreeNode> list) { if (root == null) { return; } inorderTraversal(root.left, list); list.add(root); inorderTraversal(root.right, list); } }
14,-----------------Search Range in Binary Search Tree(二叉查找樹中搜索區間)
答案:

public ArrayList<Integer> searchRange(TreeNode root, int k1, int k2) { // write your code here ArrayList<Integer> res = new ArrayList(); searchRange(root, k1, k2, res); return res; } public static void searchRange(TreeNode root, int k1, int k2, ArrayList<Integer> res) { if (root == null) { return; } if (root.val <= k2 && root.val >= k1) { searchRange(root.left, k1, k2, res); res.add(root.val); searchRange(root.right, k1, k2, res); } else if (root.val > k2) { searchRange(root.left, k1, k2, res); } else { searchRange(root.right, k1, k2, res); } }
-------------------------------------------
第二周:二分查找:
-------------------------------------------
1,Classical Binary Search(二分查找)
答案:bug-free;

public static int findPosition(int[] nums, int target) { if(nums == null || nums.length == 0) { return -1; } int start = 0; int end = nums.length - 1; while (start <= end){ int mid = start + (end - start) / 2; if (nums[mid] == target){ return mid; } else if (nums[mid] < target) { start = mid + 1; } else { end = mid - 1; } } return -1; }
2,First Position of Target(找出現的第一個位置)
答案:bug-free;

public static int binarySearch(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; } int res = -1; int start = 0; int end = nums.length - 1; while (start <= end){ int mid = start + (end - start) / 2; if (nums[mid] == target){ res = mid; end = mid - 1; } else if (nums[mid] < target) { start = mid + 1; } else { end = mid - 1; } } return res; }
3,Last Position of Target(找出現的最后一個位置)
答案: bug-free;

if(nums == null || nums.length == 0) { return -1; } int res = -1; int start = 0; int end = nums.length - 1; while (start <= end){ int mid = start + (end - start) / 2; if (nums[mid] == target){ res = mid; start = mid + 1; } else if (nums[mid] < target) { start = mid + 1; } else { end = mid - 1; } } return res;
4,sqrt(x)求平方根

x的平方根 描述 筆記 數據 評測 實現 int sqrt(int x) 函數,計算並返回 x 的平方根。 您在真實的面試中是否遇到過這個題? Yes 樣例 sqrt(3) = 1 sqrt(4) = 2 sqrt(5) = 2 sqrt(10) = 3
(1)答案和思路:利用二分來做,注意的是,要特判0.而且注意start開始位置是1.結束位置是x,如果要讓他是x/2結束,那么特判1.

class Solution { /** * @param x: An integer * @return: The sqrt of x */ public int sqrt(int x) { if (x < 0) { return -1; } if (x <= 1) { return x; } int start = 1; int end = x / 2; int res = -1; while (start <= end) { int mid = start + (end - start) / 2; if (x / mid == mid) { return mid; } else if (x / mid < mid) { end = mid - 1; } else { res = mid; start = mid + 1; } } return res; } }
(2)一刷AC所需次數2次:所犯錯誤有:1,沒有特判0,導致分母為0; 2,start從0開始,導致分母為0;
(3)二刷:bug-free;
5,search a 2D matrix (矩陣里面查找元素)
(1)答案和思路:也是用二分來找找。注意的就是,這里會用到兩次二分,所以,start,end,left,right什么的要分清楚了。
答案:

public boolean searchMatrix(int[][] matrix, int target) { if (null == matrix || matrix.length == 0) { return false; } //find row int start = 0; int end = matrix.length - 1; int row = -1; while (start <= end) { int mid = start + (end - start) / 2; if (matrix[mid][0] == target) { return true; } else if (matrix[mid][0] < target) { row = mid; start = mid + 1; } else { end = mid - 1; } } if (row == -1) { return false; } start = 0; end = matrix[row].length - 1; while (start <= end) { int mid = start + (end - start) / 2; if (matrix[row][mid] == target) { return true; } else if (matrix[row][mid] < target) { start = mid + 1; } else { end = mid - 1; } } return false; }
(2)一刷所需AC次數3次:所犯錯誤:1,end,right混淆。導致死循環。2,變量row,res混淆。
(3)二刷:bug-free;
5,search insert position(搜索要插入的位置)
(1)答案和思路:注意的是無論大小都要記下index可以取的位置
(2)一刷要AC次數3次:所犯錯誤:1,當a == null || a.length == 0時候,記住不是返回-1,而是0;
(3)二刷:bug-free;
6,Count of Smaller Number(統計比給定整數小的數的個數)
(1)答案和思路:利用二分來做。注意的地方:如果a == null。那么不能return null;所以一定要注意特判地方不要隨時返回-1;

import java.util.Arrays; public class Solution { /** * @param A: An integer array * @return: The number of element in the array that * are smaller that the given integer */ public ArrayList<Integer> countOfSmallerNumber(int[] a, int[] queries) { // write your code here ArrayList<Integer> res = new ArrayList(); if (a == null || queries == null) { return res; } Arrays.sort(a); for (int i = 0; i < queries.length; i++) { res.add(binarySearch(a, queries[i])); } return res; } public static int binarySearch(int[] a, int target) { if (a == null || a.length == 0) { return 0; } int start = 0; int end = a.length - 1; int res = 0; while (start <= end) { int mid = start + (end - start) / 2; if (a[mid] < target) { res = mid + 1; start = mid + 1; } else { end = mid - 1; } } return res; } }
(2)一刷AC需要三次。所犯錯誤a==null,return res;
(3)二刷:沒有bug-free。所犯錯誤,a==null || a.length == 0,不能return -1;
7,------------------Search for a Range(搜索區間)
答案:bug-free;

public class Solution { /** *@param A : an integer sorted array *@param target : an integer to be inserted *return : a list of length 2, [index1, index2] */ public int[] searchRange(int[] a, int target) { // write your code here int[] res = new int[2]; res[0] = -1; res[1] = -1; if (a == null || a.length == 0) { return res; } res[0] = findStartPosition(a, target); res[1] = findEndPosition(a, target); return res; } public static int findStartPosition(int[] a, int target) { if (a == null || a.length == 0) { return -1; } int start = 0; int end = a.length - 1; int res = -1; while (start <= end) { int mid = start + (end - start) / 2; if (a[mid] == target) { res = mid; end = mid - 1; } else if (a[mid] < target) { start = mid + 1; } else { end = mid - 1; } } return res; } public static int findEndPosition(int[] a, int target) { if (a == null || a.length == 0) { return -1; } int start = 0; int end = a.length - 1; int res = -1; while (start <= end) { int mid = start + (end - start) / 2; if (a[mid] == target) { res = mid; start = mid + 1; } else if (a[mid] < target) { start = mid + 1; } else { end = mid - 1; } } return res; } }
8,------------------First Bad Version(第一個錯誤的代碼版本)
答案:注意start從1開始; bug-free;

public int findFirstBadVersion(int n) { if (n < 0) { return -1; } int start = 0; int end = n; int res = -1; while (start <= end) { int mid = start + (end - start) / 2; if (SVNRepo.isBadVersion(mid)) { res = mid; end = mid - 1; } else { start = mid + 1; } } return res; }
9,-----------------Search in a Big Sorted Array(在大數組中查找)
(1)答案和思路:利用二分。注意的地方有:1,不要錯把continue用成了break。2,注意二分題目,找的往往是第一個位置

public class Solution { /** * @param reader: An instance of ArrayReader. * @param target: An integer * @return : An integer which is the index of the target number */ public int searchBigSortedArray(ArrayReader reader, int target) { // write your code here if (reader == null) { return -1; } int start = 0; int end = Integer.MAX_VALUE; int res = -1; while (start <= end) { int mid = start + (end - start) / 2; if (reader.get(mid) == -1) { end = mid - 1; continue; } if (reader.get(mid) == target) { res = mid; end = mid - 1; } else if (reader.get(mid) < target) { start = mid + 1; } else { end = mid - 1; } } return res; } }
(2)一刷AC需要次數:3次;所犯錯誤有:1,break;2,沒有注意有重復元素。
(3)二刷:bug-free;
10,-------------Find Minimum in Rotated Sorted Array(尋找旋轉排序數組中的最小值)
答案:注意end的位置是length - 1;bug-free;

public static int findMin(int[] num) { int res = Integer.MAX_VALUE; if (num == null || num.length == 0) { return -1; } if (num[0] < num[num.length - 1]) { return num[0]; } else { int start = 0; int end = num.length - 1; // error while (start <= end) { int mid = start + (end - start) / 2; if (num[mid] <= num[end]) { res = Math.min(res, num[mid]); end = mid - 1; } else { start = mid + 1; } } } return res; }
11,------------Search in Rotated Sorted Array(搜索旋轉排序數組)
答案:記住end從length - 1開始。bug-free;

public class Solution { /** *@param A : an integer rotated sorted array *@param target : an integer to be searched *return : an integer */ public int search(int[] a, int target) { // write your code here if (a == null || a.length == 0) { return -1; } int start = 0; int end = a.length - 1; while (start <= end) { int mid = start + (end - start) / 2; if (a[mid] == target) { return mid; } else if (a[mid] <= a[end]) { if (target > a[mid] && target <= a[end]) { start = mid + 1; } else { end = mid - 1; } } else { if (target >= a[start] && target < a[mid]) { end = mid - 1; } else { start = mid + 1; } } } return -1; } }
12,---------------Find Peak Element( 尋找峰值)
答案:切記不要忽略了最重要的條件,不然代碼好長好丑。bug-free;

class Solution { /** * @param A: An integers array. * @return: return any of peek positions. */ public int findPeak(int[] a) { // write your code here if (a == null || a.length <= 2) { return -1; } int start = 1; int end = a.length -2; while (start <= end) { int mid = start + (end - start) / 2; if (a[mid] > a[mid - 1] && a[mid] > a[mid + 1]) { return mid; } else if (a[mid] < a[mid - 1]) { end = mid - 1; } else { start = mid + 1; } } return -1; } }
13,-----------Recover Rotated Sorted Array(恢復旋轉排序數組)
(1)答案:注意最新答案,利用了二分查到到開始,然后利用逆序的逆序來解題。這是不重復元素的做法。是錯的。

public class Solution { /** * @param nums: The rotated sorted array * @return: void */ public static void recoverRotatedSortedArray(ArrayList<Integer> nums) { int index = findMin(nums); reverseList(nums, 0, index - 1); reverseList(nums, index, nums.size() - 1); reverseList(nums, 0, nums.size() - 1); } public static void reverseList(ArrayList<Integer> num, int start, int end) { for (int i = start, j = end; i < j; i++, j--) { int temp = num.get(i); num.set(i, num.get(j)); num.set(j, temp); } } public static int findMin(ArrayList<Integer> num) { int res = Integer.MAX_VALUE; int ans = 0; if (num == null || num.size() == 0) { return -1; } if (num.get(0) < num.get(num.size() - 1)) { return 0; } else { int start = 0; int end = num.size() - 1; // error while (start <= end) { int mid = start + (end - start) / 2; if (num.get(mid) <= num.get(end)) { if (num.get(mid) < res) { res = num.get(mid); ans = mid; } end = mid - 1; } else { start = mid + 1; } } } return ans; } }
當含有重復元素的時候, 答案是這個:

import java.util.ArrayList; public class Solution { /** * @param nums: The rotated sorted array * @return: void */ public void recoverRotatedSortedArray(ArrayList<Integer> nums) { // write your code if (null == nums || nums.size() == 0) { return; } for (int i = 0; i < nums.size() - 1; i++) { if (nums.get(i) > nums.get(i + 1)) { reverseArrayList(nums, 0, i); reverseArrayList(nums, i + 1, nums.size() - 1); reverseArrayList(nums, 0, nums.size() - 1); } } } // reverse the ArrayList public static void reverseArrayList(ArrayList<Integer> a, int start, int end) { // swap the a.get(start) and the a.get(end) /** Error: *while (start < end) { * a.get(start) = a.get(start) ^ a.get(end); * a.get(end) = a.get(start) ^ a.get(end); * a.get(start) = a.get(start) ^ a.get(end); * start++; * end--; * } */ while (start < end) { a.set(start, a.get(start) ^ a.get(end)); a.set(end, a.get(start) ^ a.get(end)); a.set(start, a.get(start) ^ a.get(end)); start++; end--; } } }
(2)一刷不能bug-free,因為用了二分來做,是錯的。
(3)二刷,bug-free;
14.-------------Rotate String(旋轉字符串)
(1)答案

public class Solution { /** * @param str: an array of char * @param offset: an integer * @return: nothing */ public void rotateString(char[] str, int offset) { if (null == str || str.length <= 1) { return; } offset = offset % str.length; reverseString(str, 0, str.length - offset - 1); reverseString(str, str.length - offset, str.length - 1); reverseString(str, 0, str.length - 1); } public static void reverseString(char[] str, int start, int end) { if (null == str || str.length == 0) { return; } while (start < end) { // Error: // str[start] = str[start] ^ str[end]; // str[end] = str[start] ^ str[end]; // str[start] = str[start] ^str[end]; str[start] = (char) (str[start] ^ str[end]); str[end] = (char) (str[start] ^ str[end]); str[start] = (char) (str[start] ^ str[end]); start++; end--; } } }
(2)1刷需要3次才能AC:因為,1:當字符串^時候結果得到的是整數,需要(char);
(2)2刷。bug-free;
-------------------------------------------
第一周:入門式。字符串,子集,排列
-------------------------------------------
1,strStr(字符串查找)

字符串查找
描述
筆記
數據
評測
對於一個給定的 source 字符串和一個 target 字符串,你應該在 source 字符串中找出 target 字符串出現的第一個位置(從0開始)。如果不存在,則返回 -1。
(1)答案和思路:就是常規的一一對比,注意一下循環的結束位置即可。

import java.lang.String; class Solution { /** * Returns a index to the first occurrence of target in source, * or -1 if target is not part of source. * @param source string to be scanned. * @param target string containing the sequence of characters to match. */ public int strStr(String source, String target) { if (source == null || target == null) { return -1; } for (int i = 0; i < source.length() - target.length() + 1; i++) { int j = 0; for (j = 0; j < target.length(); j++) { if (source.charAt(i + j) != target.charAt(j)) { break; } } if (j == target.length()) { return i; } } return -1; } }
(2)AC所需次數:4.所犯錯誤有:String的length,應該是length(); 第一個字符串的終止位置應該是他的長度減去比較字符串的長度。所以注意不是<而是<=。import java.lang.String;
(3) 二刷AC所需次數:bug-free;
2,Subsets(子集)

子集
描述
筆記
數據
評測
給定一個含不同整數的集合,返回其所有的子集
注意事項
子集中的元素排列必須是非降序的,解集必須不包含重復的子集
(1)答案和思路:每放入一個元素,除了保留已有的那些子集之外,在這些已有的每一個子集上加上新來的元素。

import java.util.ArrayList; import java.util.Arrays; class Solution { /** * @param S: A set of numbers. * @return: A list of lists. All valid subsets. */ public ArrayList<ArrayList<Integer>> subsets(int[] nums) { Arrays.sort(nums); ArrayList<ArrayList<Integer>> subsets = new ArrayList(); ArrayList<Integer> subset = new ArrayList(); subsets.add(subset); if (nums == null || nums.length == 0) { return subsets; } for (int i = 0; i < nums.length; i++) { ArrayList<ArrayList<Integer>> tempSubsets = new ArrayList(subsets); for (ArrayList<Integer> preSubset : subsets) { ArrayList<Integer> curSubset = new ArrayList(preSubset); curSubset.add(nums[i]); tempSubsets.add(curSubset); } subsets = new ArrayList(tempSubsets); } return subsets; } }
(2)一刷AC所需次數:3次。所犯錯誤:加入新來的元素,在遍歷的時候加,那樣已經改變了原來的子集。所以要遍歷出一個子集,然后new出一個子集,再add。 還有一個錯誤就是因為要保證子集的非降序,所以要先排序。
(3) 二刷AC所需次數:2次。所犯錯誤:sort。
3,Subsets II(帶重復元素的子集)

帶重復元素的子集
描述
筆記
數據
評測
給定一個可能具有重復數字的列表,返回其所有可能的子集
(1)答案和思路:與1不同的地方就是在add進去的時候,用一下contains。

import java.util.ArrayList; import java.util.Collections; class Solution { /** * @param S: A set of numbers. * @return: A list of lists. All valid subsets. */ public ArrayList<ArrayList<Integer>> subsetsWithDup(ArrayList<Integer> s){ ArrayList<ArrayList<Integer>> subsets = new ArrayList(); ArrayList<Integer> subset = new ArrayList(); subsets.add(subset); if (s == null || s.size() == 0) { return subsets; } Collections.sort(s); for (int num : s) { ArrayList<ArrayList<Integer>> tempSubsets = new ArrayList(subsets); for (ArrayList<Integer> preSubset : subsets) { ArrayList<Integer> curSubset = new ArrayList(preSubset); curSubset.add(num); if (!tempSubsets.contains(curSubset)) { tempSubsets.add(curSubset); } } subsets = new ArrayList(tempSubsets); } return subsets; } }
(2)一刷AC需要三次,所犯錯誤為:1,忘記排序;2,不會對list排序:import java.util.Collections; Collections.sort(list);3,tempSubsets 錯寫temp;
(3)二刷:bug-free;
4,Permutations(全排列)

全排列
描述
筆記
數據
評測
給定一個數字列表,返回其所有可能的排列。
(1)答案和思路:就是每次來一個數,就把他插入到已有的permutation中。

import java.util.ArrayList; class Solution { /** * @param nums: A list of integers. * @return: A list of permutations. */ public ArrayList<ArrayList<Integer>> permute(ArrayList<Integer> nums) { // write your code here ArrayList<ArrayList<Integer>> permutations = new ArrayList(); ArrayList<Integer> permutation = new ArrayList(); if (nums == null || nums.size() == 0) { return permutations; } permutations.add(permutation); for (int num : nums) { ArrayList<ArrayList<Integer>> tempPermutations = new ArrayList(); for (ArrayList<Integer> prePermutation : permutations) { for (int i = 0; i <= prePermutation.size(); i++) { ArrayList<Integer> curPermutation = new ArrayList(prePermutation); curPermutation.add(i, num); tempPermutations.add(curPermutation); } } permutations = new ArrayList(tempPermutations); } return permutations; } }
(2)一刷AC需要兩次:所犯錯誤:1,當輸入null的時候全排列和子集是不一樣的,子集是[[]]。全排列是[],也就是[null]。所以,在判斷之前不能add任何東西。
(3)二刷:bug-free;
5,Permutations(帶重復元素的全排列)

帶重復元素的排列
描述
筆記
數據
評測
給出一個具有重復數字的列表,找出列表所有不同的排列。
(1)答案和思路:在add之前進行一次判斷就可以了。

import java.util.ArrayList; class Solution { /** * @param nums: A list of integers. * @return: A list of permutations. */ public ArrayList<ArrayList<Integer>> permuteUnique(ArrayList<Integer> nums) { // write your code here ArrayList<ArrayList<Integer>> permutations = new ArrayList(); ArrayList<Integer> permutation = new ArrayList(); if (nums == null || nums.size() == 0) { return permutations; } permutations.add(permutation); for (int num : nums) { ArrayList<ArrayList<Integer>> tempPermutations = new ArrayList(); for (ArrayList<Integer> prePermutation : permutations) { for (int i = 0; i <= prePermutation.size(); i++) { ArrayList<Integer> curPermutation = new ArrayList(prePermutation); curPermutation.add(i, num); if (!tempPermutations.contains(curPermutation)) { tempPermutations.add(curPermutation); } } } permutations = new ArrayList(tempPermutations); } return permutations; } }
(2)一刷:bug-free;