(lintcode全部题目解答之)(附容易犯的错误)
---------------------------------------------------------------
本文使用方法:所有题目,只需要把标题输入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;