LeetCode
310. Minimum Height Trees
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
For a undirected graph with tree characteristics, we can choose any node as the root. The result graph is then a rooted tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs). Given such a graph, write a function to find all the MHTs and return a list of their root labels. Format The graph contains n nodes which are labeled from 0 to n - 1. You will be given the number n and a list of undirected edges (each edge is a pair of labels). You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges. Example 1: Given n = 4, edges = [[1, 0], [1, 2], [1, 3]] 0 | 1 / \ 2 3 return [1] Example 2: Given n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]] 0 1 2 \ | / 3 | 4 | 5 return [3, 4]
思路:普通的暴力BFS会超时,即对每个节点用BFS统计以该节点为根的树高,然后排序输出结果。现从树的度入手,统计每个节点的度,然后度为1的入列(删除该节点),出列后将该节点的度置为0,对该节点的每个邻居节点,若邻居节点度为0,说明已经访问过,continue掉,否则度减1,如果度为1就入列,否则继续。注意当只有一个节点时的特殊情况,此时是图,所以结果是包含0的list而不是空list!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<Integer> findMinHeightTrees(int n, int[][] edges) { 3 List<Integer> result = new ArrayList<>(); 4 //边数不等于顶点数 - 1不是图 5 if (n <= 0 || edges.length != n - 1) { 6 return result; 7 } 8 //特殊情况,只有一个孤立节点,是图。 9 if (n == 1) { 10 result.add(0); 11 return result; 12 } 13 Map<Integer, List<Integer>> map = new HashMap<>(); 14 int[] inDegree = new int[n]; 15 for (int i = 0; i < n; i++) { 16 map.put(i, new ArrayList<Integer>()); 17 } 18 for (int i = 0; i < edges.length; i++) { 19 map.get(edges[i][0]).add(edges[i][1]); 20 map.get(edges[i][1]).add(edges[i][0]); 21 inDegree[edges[i][0]]++; 22 inDegree[edges[i][1]]++; 23 } 24 Queue<Integer> queue = new LinkedList<>(); 25 for (int i = 0; i < n; i++) { 26 //大于等于2个节点,只要有一个节点的度为0,说明不连通,不是图 27 if (inDegree[i] == 0) { 28 return result; 29 } 30 if (inDegree[i] == 1) { 31 queue.offer(i); 32 } 33 } 34 while (!queue.isEmpty()) { 35 int size = queue.size(); 36 result = new ArrayList<Integer>(); 37 for (int i = 0; i < size; i++) { 38 int num = queue.poll(); 39 result.add(num); 40 inDegree[num]--; 41 for (int temp : map.get(num)) { 42 if (inDegree[temp] == 0) { 43 continue; 44 } 45 inDegree[temp]--; 46 if (inDegree[temp] == 1) { 47 queue.offer(temp); 48 } 49 } 50 } 51 } 52 return result; 53 } 54 }
524. Longest Word in Dictionary through Deleting
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a string and a string dictionary, find the longest string in the dictionary that can be formed by deleting some characters of the given string. If there are more than one possible results, return the longest word with the smallest lexicographical order. If there is no possible result, return the empty string. Example 1: Input: s = "abpcplea", d = ["ale","apple","monkey","plea"] Output: "apple" Example 2: Input: s = "abpcplea", d = ["a","b","c"] Output: "a" Note: All the strings in the input will only contain lower-case letters. The size of the dictionary won't exceed 1,000. The length of all the strings in the input won't exceed 1,000.
注意此题比较一个单词是否能由另一个单词经过删掉一些字符来构造的算法思想!
思路I:先按题目要求排序,然后依次取出词典里的词,如果该词可以被构造出,则返回该词,介绍,可以在中间就可以结束而不必遍历词典里的所有词。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public String findLongestWord(String s, List<String> d) { 3 if (s == null || s.length() == 0 || d == null || d.size() == 0) { 4 return ""; 5 } 6 Collections.sort(d, new Comparator<String>() { 7 public int compare(String a, String b) { 8 if (a.length() != b.length()) { 9 return b.length() - a.length(); 10 } 11 return a.compareTo(b); 12 } 13 }); 14 for (String str : d) { 15 int index = 0; 16 for (int i = 0; i < s.length(); i++) { 17 if (index < str.length() && s.charAt(i) == str.charAt(index)) { 18 index++; 19 } 20 } 21 if (index == str.length()) { 22 return str; 23 } 24 } 25 return ""; 26 } 27 }
思路II:不排序,依次取出词典里的词,如果该词可以被构造出,此时满足以下两种情况方可更新长度最长且按字典排序最小的词longest。1.该词长度大于longest 2.该词长度等于longest,但是该词按字典顺序排序的结果小于longest。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public String findLongestWord(String s, List<String> d) { 3 if (s == null || s.length() == 0 || d == null || d.size() == 0) { 4 return ""; 5 } 6 String longest = ""; 7 for (String str : d) { 8 int index = 0; 9 for (int i = 0; i < s.length(); i++) { 10 if (index < str.length() && s.charAt(i) == str.charAt(index)) { 11 index++; 12 } 13 } 14 if (index == str.length() && str.length() >= longest.length()) { 15 if (str.length() > longest.length()) { 16 longest = str; 17 } 18 if (str.compareTo(longest) < 0) { 19 longest = str; 20 } 21 } 22 } 23 return longest; 24 } 25 }
31. Next Permutation
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers. If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order). The replacement must be in-place, do not allocate extra memory. Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column. 1,2,3 → 1,3,2 3,2,1 → 1,2,3 1,1,5 → 1,5,1
思路:从后往前找一个数比后一个数小的位置,然后从后往前找到第一个大于该数的位置并与该数交换,然后翻转该数原来的索引到数组末尾的元素。如果找不到该数,说明元素已从大到小排好序,直接reverse即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public void nextPermutation(int[] nums) { 3 if (nums == null || nums.length == 0) { 4 return; 5 } 6 for (int i = nums.length - 2; i >= 0; i--) { 7 if (nums[i] < nums[i + 1]) { 8 for (int j = nums.length - 1; j > i; j--) { 9 if (nums[j] > nums[i]) { 10 swap(nums, i, j); 11 reverse(nums, i + 1, nums.length - 1); 12 return; 13 } 14 } 15 } 16 } 17 reverse(nums, 0, nums.length - 1); 18 return; 19 } 20 public void swap(int[] nums, int i, int j) { 21 int temp = nums[i]; 22 nums[i] = nums[j]; 23 nums[j] = temp; 24 } 25 public void reverse(int[] nums, int start, int end) { 26 int left = start; 27 int right = end; 28 while (left < right) { 29 swap(nums, left, right); 30 left++; 31 right--; 32 } 33 } 34 }
297. Serialize and Deserialize Binary Tree
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment. Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure. For example, you may serialize the following tree 1 / \ 2 3 / \ 4 5 as "[1,2,3,null,null,4,5]", just the same as how LeetCode OJ serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with different approaches yourself. Note: Do not use class member/global/static variables to store states. Your serialize and deserialize algorithms should be stateless.
思路:序列化和反序列化都用List存放树节点。序列化要注意的就是去掉List尾部多余的null,这部分没必要不用序列化。反序列化要注意的是用leftChild来指示当前遍历的节点是父节点的左儿子还是右儿子,用index索引指示当前遍历的节点的父节点,只有遍历了右节点之后(leftChild指针为false)index才++,表示父节点为下一位。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Codec { 11 12 // Encodes a tree to a single string. 13 public String serialize(TreeNode root) { 14 if (root == null) { 15 return "[]"; 16 } 17 List<TreeNode> buffer = new ArrayList<>(); 18 buffer.add(root); 19 for (int i = 0; i < buffer.size(); i++) { 20 TreeNode node = buffer.get(i); 21 if (node != null) { 22 buffer.add(node.left); 23 buffer.add(node.right); 24 } 25 } 26 while (buffer.get(buffer.size() - 1) == null) { 27 buffer.remove(buffer.size() - 1); 28 } 29 StringBuilder sb = new StringBuilder(); 30 sb.append("[").append(buffer.get(0).val); 31 for (int i = 1; i < buffer.size(); i++) { 32 if (buffer.get(i) != null) { 33 sb.append(",").append(buffer.get(i).val); 34 } else { 35 sb.append(",").append("null"); 36 } 37 } 38 sb.append("]"); 39 return sb.toString(); 40 } 41 42 // Decodes your encoded data to tree. 43 public TreeNode deserialize(String data) { 44 if (data == null || data.length() <= 2) { 45 return null; 46 } 47 String[] vals = data.substring(1, data.length() - 1).split(","); 48 TreeNode root = new TreeNode(Integer.parseInt(vals[0])); 49 List<TreeNode> buffer = new ArrayList<>(); 50 buffer.add(root); 51 boolean leftChild = true; 52 int index = 0; 53 for (int i = 1; i < vals.length; i++) { 54 if (!vals[i].equals("null")) { 55 TreeNode node = new TreeNode(Integer.parseInt(vals[i])); 56 if (leftChild) { 57 buffer.get(index).left = node; 58 } else { 59 buffer.get(index).right = node; 60 } 61 buffer.add(node); 62 } 63 if (!leftChild) { 64 index++; 65 } 66 leftChild = !leftChild; 67 } 68 return root; 69 } 70 } 71 72 // Your Codec object will be instantiated and called as such: 73 // Codec codec = new Codec(); 74 // codec.deserialize(codec.serialize(root));
55. Jump Game
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of non-negative integers, you are initially positioned at the first index of the array. Each element in the array represents your maximum jump length at that position. Determine if you are able to reach the last index. For example: A = [2,3,1,1,4], return true. A = [3,2,1,0,4], return false.
思路:用一farthest索引表示能达到的最大索引是多少,最后只要判断farthest >= nums.length - 1即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean canJump(int[] nums) { 3 if (nums == null || nums.length == 0) { 4 return true; 5 } 6 int farthest = 0; 7 for (int i = 0; i < nums.length; i++) { 8 if (i <= farthest && i + nums[i] > farthest) { 9 farthest = i + nums[i]; 10 } 11 } 12 return farthest >= nums.length - 1; 13 } 14 }
45. Jump Game II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of non-negative integers, you are initially positioned at the first index of the array. Each element in the array represents your maximum jump length at that position. Your goal is to reach the last index in the minimum number of jumps. For example: Given array A = [2,3,1,1,4] The minimum number of jumps to reach the last index is 2. (Jump 1 step from index 0 to 1, then 3 steps to the last index.)
思路:注意当数组只有一个元素即起点和终点相同时最小跳数是0,这种特殊情况不能在循环里面算,否则出错。每次都计算最远到哪里,然后从start到end依次遍历计算下一次最远到哪里。跳数++。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int jump(int[] nums) { 3 if (nums == null || nums.length <= 1) { 4 return 0; 5 } 6 int start = 0; 7 int end = 0; 8 int farthest = 0; 9 int steps = 0; 10 while (start <= end) { 11 steps++; 12 for (int i = start; i <= end; i++) { 13 if (i +nums[i] > farthest) { 14 farthest = i + nums[i]; 15 } 16 } 17 if (farthest >= nums.length - 1) { 18 return steps; 19 } 20 start = end + 1; 21 end = farthest; 22 } 23 return -1; 24 } 25 }
51. N-Queens
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other. Given an integer n, return all distinct solutions to the n-queens puzzle. Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively. Example There exist two distinct solutions to the 4-queens puzzle: [ // Solution 1 [".Q..", "...Q", "Q...", "..Q." ], // Solution 2 ["..Q.", "Q...", "...Q", ".Q.." ] ]
思路:注意tempList存放的是每个皇后的索引,当tempList装满后再根据tempList中的索引画出棋盘加入到results中。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<List<String>> solveNQueens(int n) { 3 List<List<String>> results = new ArrayList<>(); 4 if (n < 1) { 5 return results; 6 } 7 dfsHelper(n, new ArrayList<Integer>(), results); 8 return results; 9 } 10 public void dfsHelper(int n, List<Integer> tempList, List<List<String>> results) { 11 if (tempList.size() == n) { 12 results.add(drawChessBoard(tempList)); 13 return; 14 } 15 for (int i = 0; i < n; i++) { 16 if (!valid(i, tempList)) { 17 continue; 18 } 19 tempList.add(i); 20 dfsHelper(n, tempList, results); 21 tempList.remove(tempList.size() - 1); 22 } 23 } 24 public boolean valid(int index, List<Integer> tempList) { 25 int size = tempList.size(); 26 for (int i = 0; i < size; i++) { 27 if (tempList.get(i) == index) { 28 return false; 29 } 30 if (i - tempList.get(i) == size - index) { 31 return false; 32 } 33 if (i + tempList.get(i) == size + index) { 34 return false; 35 } 36 } 37 return true; 38 } 39 public List<String> drawChessBoard(List<Integer> tempList) { 40 List<String> result = new ArrayList<>(); 41 for (int i = 0; i < tempList.size(); i++) { 42 StringBuilder sb = new StringBuilder(); 43 for (int j = 0; j < tempList.size(); j++) { 44 if (j == tempList.get(i)) { 45 sb.append("Q"); 46 } else { 47 sb.append("."); 48 } 49 } 50 result.add(sb.toString()); 51 } 52 return result; 53 } 54 }
52. N-Queens II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Follow up for N-Queens problem. Now, instead outputting board configurations, return the total number of distinct solutions.
思路:同N-Queens,只需要设置一个全局变量solutions来统计方案的个数即可,不用创建results了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int solutions = 0; 3 public int totalNQueens(int n) { 4 if (n < 1) { 5 return 0; 6 } 7 dfsHelper(n, new ArrayList<Integer>()); 8 return solutions; 9 } 10 public void dfsHelper(int n, List<Integer> tempList) { 11 if (tempList.size() == n) { 12 solutions++; 13 return; 14 } 15 for (int i = 0; i < n; i++) { 16 if (!valid(i, tempList)) { 17 continue; 18 } 19 tempList.add(i); 20 dfsHelper(n, tempList); 21 tempList.remove(tempList.size() - 1); 22 } 23 } 24 public boolean valid(int index, List<Integer> tempList) { 25 int size = tempList.size(); 26 for (int i = 0; i < size; i++) { 27 if (tempList.get(i) == index) { 28 return false; 29 } 30 if (i - tempList.get(i) == size - index) { 31 return false; 32 } 33 if (i + tempList.get(i) == size + index) { 34 return false; 35 } 36 } 37 return true; 38 } 39 }
232. Implement Queue using Stacks
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Implement the following operations of a queue using stacks. push(x) -- Push element x to the back of queue. pop() -- Removes the element from in front of queue. peek() -- Get the front element. empty() -- Return whether the queue is empty. Notes: You must use only standard operations of a stack -- which means only push to top, peek/pop from top, size, and is empty operations are valid. Depending on your language, stack may not be supported natively. You may simulate a stack by using a list or deque (double-ended queue), as long as you use only standard operations of a stack. You may assume that all operations are valid (for example, no pop or peek operations will be called on an empty queue).
思路:用两个栈来实现队列。stack1用来push元素,stack2用来pop元素,stack2执行pop操作元素不够的时候将stack1里的元素倒入stack2即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class MyQueue { 2 Stack<Integer> stack1; 3 Stack<Integer> stack2; 4 5 /** Initialize your data structure here. */ 6 public MyQueue() { 7 stack1 = new Stack<>(); 8 stack2 = new Stack<>(); 9 } 10 11 /** Push element x to the back of queue. */ 12 public void push(int x) { 13 stack1.push(x); 14 } 15 16 /** Removes the element from in front of queue and returns that element. */ 17 public int pop() { 18 if (!stack2.isEmpty()) { 19 return stack2.pop(); 20 } 21 while (!stack1.isEmpty()) { 22 stack2.push(stack1.pop()); 23 } 24 if (!stack2.isEmpty()) { 25 return stack2.pop(); 26 } 27 return 0; 28 } 29 30 /** Get the front element. */ 31 public int peek() { 32 if (!stack2.isEmpty()) { 33 return stack2.peek(); 34 } 35 while (!stack1.isEmpty()) { 36 stack2.push(stack1.pop()); 37 } 38 if (!stack2.isEmpty()) { 39 return stack2.peek(); 40 } 41 return 0; 42 } 43 44 /** Returns whether the queue is empty. */ 45 public boolean empty() { 46 return stack1.isEmpty() && stack2.isEmpty(); 47 } 48 } 49 50 /** 51 * Your MyQueue object will be instantiated and called as such: 52 * MyQueue obj = new MyQueue(); 53 * obj.push(x); 54 * int param_2 = obj.pop(); 55 * int param_3 = obj.peek(); 56 * boolean param_4 = obj.empty(); 57 */
225. Implement Stack using Queues
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Implement the following operations of a stack using queues. push(x) -- Push element x onto stack. pop() -- Removes the element on top of the stack. top() -- Get the top element. empty() -- Return whether the stack is empty. Notes: You must use only standard operations of a queue -- which means only push to back, peek/pop from front, size, and is empty operations are valid. Depending on your language, queue may not be supported natively. You may simulate a queue by using a list or deque (double-ended queue), as long as you use only standard operations of a queue. You may assume that all operations are valid (for example, no pop or top operations will be called on an empty stack).
思路:用两个队列实现栈。queue1用来执行所有操作,queue2作为辅助队列用来辅助pop和top操作,不需要把queue2里的元素出列然后入列给queue1,只需要交换两个队列即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class MyStack { 2 Queue<Integer> queue1; 3 Queue<Integer> queue2; 4 /** Initialize your data structure here. */ 5 public MyStack() { 6 queue1 = new LinkedList<>(); 7 queue2 = new LinkedList<>(); 8 } 9 10 /** Push element x onto stack. */ 11 public void push(int x) { 12 queue1.offer(x); 13 } 14 15 /** Removes the element on top of the stack and returns that element. */ 16 public int pop() { 17 if (queue1.size() == 0) { 18 return 0; 19 } 20 int size = queue1.size(); 21 for (int i = 0; i < size - 1; i++) { 22 queue2.offer(queue1.poll()); 23 } 24 int result = queue1.poll(); 25 Queue<Integer> temp = queue1; 26 queue1 = queue2; 27 queue2 = temp; 28 return result; 29 } 30 31 /** Get the top element. */ 32 public int top() { 33 if (queue1.size() == 0) { 34 return 0; 35 } 36 int size = queue1.size(); 37 for (int i = 0; i < size - 1; i++) { 38 queue2.offer(queue1.poll()); 39 } 40 int result = queue1.peek(); 41 queue2.offer(queue1.poll()); 42 Queue<Integer> temp = queue1; 43 queue1 = queue2; 44 queue2 = temp; 45 return result; 46 } 47 48 /** Returns whether the stack is empty. */ 49 public boolean empty() { 50 return queue1.isEmpty(); 51 } 52 } 53 54 /** 55 * Your MyStack object will be instantiated and called as such: 56 * MyStack obj = new MyStack(); 57 * obj.push(x); 58 * int param_2 = obj.pop(); 59 * int param_3 = obj.top(); 60 * boolean param_4 = obj.empty(); 61 */
155. Min Stack
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. push(x) -- Push element x onto stack. pop() -- Removes the element on top of the stack. top() -- Get the top element. getMin() -- Retrieve the minimum element in the stack. Example: MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); --> Returns -3. minStack.pop(); minStack.top(); --> Returns 0. minStack.getMin(); --> Returns -2.
思路:维护两个栈,一个普通stack,一个minStack用来存最小元素。每次操作都更新两个stack,两个栈的size时刻一样。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class MinStack { 2 Stack<Integer> stack; 3 Stack<Integer> minStack; 4 /** initialize your data structure here. */ 5 public MinStack() { 6 stack = new Stack<>(); 7 minStack = new Stack<>(); 8 } 9 10 public void push(int x) { 11 stack.push(x); 12 if (minStack.isEmpty()) { 13 minStack.push(x); 14 } else { 15 if (x < minStack.peek()) { 16 minStack.push(x); 17 } else { 18 minStack.push(minStack.peek()); 19 } 20 } 21 } 22 23 public void pop() { 24 stack.pop(); 25 minStack.pop(); 26 } 27 28 public int top() { 29 return stack.peek(); 30 } 31 32 public int getMin() { 33 if (minStack.isEmpty()) { 34 return 0; 35 } 36 return minStack.peek(); 37 } 38 } 39 40 /** 41 * Your MinStack object will be instantiated and called as such: 42 * MinStack obj = new MinStack(); 43 * obj.push(x); 44 * obj.pop(); 45 * int param_3 = obj.top(); 46 * int param_4 = obj.getMin(); 47 */
341. Flatten Nested List Iterator
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a nested list of integers, implement an iterator to flatten it. Each element is either an integer, or a list -- whose elements may also be integers or other lists. Example 1: Given the list [[1,1],2,[1,1]], By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,1,2,1,1]. Example 2: Given the list [1,[4,[6]]], By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,4,6].
思路:用栈来保存,hasNext的时候记得把栈顶元素(如果栈顶元素不是Integer的话)flatten(即执行pushListToStack直到栈顶元素为Integer)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * // This is the interface that allows for creating nested lists. 3 * // You should not implement it, or speculate about its implementation 4 * public interface NestedInteger { 5 * 6 * // @return true if this NestedInteger holds a single integer, rather than a nested list. 7 * public boolean isInteger(); 8 * 9 * // @return the single integer that this NestedInteger holds, if it holds a single integer 10 * // Return null if this NestedInteger holds a nested list 11 * public Integer getInteger(); 12 * 13 * // @return the nested list that this NestedInteger holds, if it holds a nested list 14 * // Return null if this NestedInteger holds a single integer 15 * public List<NestedInteger> getList(); 16 * } 17 */ 18 public class NestedIterator implements Iterator<Integer> { 19 Stack<NestedInteger> stack; 20 public NestedIterator(List<NestedInteger> nestedList) { 21 stack = new Stack<>(); 22 pushListToStack(nestedList); 23 } 24 public void pushListToStack(List<NestedInteger> nestedList) { 25 Stack<NestedInteger> temp = new Stack<>(); 26 for (NestedInteger nested : nestedList) { 27 temp.push(nested); 28 } 29 while (!temp.isEmpty()) { 30 stack.push(temp.pop()); 31 } 32 } 33 34 @Override 35 public Integer next() { 36 if (!hasNext()) { 37 return null; 38 } 39 return stack.pop().getInteger(); 40 } 41 42 @Override 43 public boolean hasNext() { 44 while (!stack.isEmpty() && !stack.peek().isInteger()) { 45 pushListToStack(stack.pop().getList()); 46 } 47 return !stack.isEmpty(); 48 } 49 } 50 51 /** 52 * Your NestedIterator object will be instantiated and called as such: 53 * NestedIterator i = new NestedIterator(nestedList); 54 * while (i.hasNext()) v[f()] = i.next(); 55 */
84. Largest Rectangle in Histogram
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram. For example, Given heights = [2,1,5,6,2,3], return 10.
思路:用栈来保存索引,在数组末尾插入0,只要遇到当前高度小于等于前一个高度,就计算前面能围成的矩形面积并更新max,注意计算矩形宽度的时候要根据栈是否为空来取值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int largestRectangleArea(int[] heights) { 3 if (heights == null || heights.length == 0) { 4 return 0; 5 } 6 Stack<Integer> stack = new Stack<>(); 7 int max = 0; 8 for (int i = 0; i <= heights.length; i++) { 9 int cur = i == heights.length ? 0 : heights[i]; 10 while (!stack.isEmpty() && cur <= heights[stack.peek()]) { 11 int h = heights[stack.pop()]; 12 int w = stack.isEmpty() ? i : i - 1 - stack.peek(); 13 max = Math.max(max, h * w); 14 } 15 stack.push(i); 16 } 17 return max; 18 } 19 }
215. Kth Largest Element in an Array
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element. For example, Given [3,2,1,5,6,4] and k = 2, return 5. Note: You may assume k is always valid, 1 ≤ k ≤ array's length.
思路I:最小堆。时间复杂度O(nlgk),空间复杂度O(k)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int findKthLargest(int[] nums, int k) { 3 if (nums == null || nums.length == 0 || k < 1 || k > nums.length) { 4 return 0; 5 } 6 PriorityQueue<Integer> pq = new PriorityQueue<>(); 7 for (int i = 0; i < nums.length; i++) { 8 if (pq.size() < k) { 9 pq.offer(nums[i]); 10 } else { 11 if (nums[i] > pq.peek()) { 12 pq.poll(); 13 pq.offer(nums[i]); 14 } 15 } 16 } 17 return pq.peek(); 18 } 19 }
思路II:partition。求第K大因此要按从大到小partition,partition中不要有=号,只有">"和"<"。时间复杂度O(n),空间复杂度O(1)。
Time Complexity: average = O(n); worst case O(n^2), O(1) space
Time Complexity O(n)来自于O(n) + O(n/2) + O(n/4) + ... ~ O(2n),此时每次partition的pivot大约将区间对半分。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int findKthLargest(int[] nums, int k) { 3 if (nums == null || nums.length == 0 || k < 1 || k > nums.length) { 4 return 0; 5 } 6 return partition(nums, 0, nums.length - 1, k); 7 } 8 public int partition(int[] nums, int start, int end, int k) { 9 if (start == end) { 10 return nums[start]; 11 } 12 int mid = nums[start + (end - start) / 2]; 13 int left = start; 14 int right = end; 15 while (left <= right) { 16 while (left <= right && nums[left] > mid) { 17 left++; 18 } 19 while (left <= right && nums[right] < mid) { 20 right--; 21 } 22 if (left <= right) { 23 int temp = nums[left]; 24 nums[left] = nums[right]; 25 nums[right] = temp; 26 left++; 27 right--; 28 } 29 } 30 if (k <= right - start + 1) { 31 return partition(nums, start, right, k); 32 } 33 if (k >= left - start + 1) { 34 return partition(nums, left, end, k - (left - start)); 35 } 36 return nums[left - 1]; 37 } 38 }
239. Sliding Window Maximum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. For example, Given nums = [1,3,-1,-3,5,3,6,7], and k = 3. Window position Max --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7 Therefore, return the max sliding window as [3,3,5,5,6,7]. Note: You may assume k is always valid, ie: 1 ≤ k ≤ input array's size for non-empty array.
思路:双端队列Deque来存储可能的最大值,入列的时候把小于当前值的元素全部出列(这些元素不可能为最大值),出列时如果要出列的元素的值等于队列首元素的值(最大值),则把这个最大元素出列。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int[] maxSlidingWindow(int[] nums, int k) { 3 if (nums == null || nums.length == 0 || k < 1 || k > nums.length) { 4 return new int[0]; 5 } 6 int[] result = new int[nums.length - k + 1]; 7 Deque<Integer> deque = new ArrayDeque<>(); 8 for (int i = 0; i < k - 1; i++) { 9 inQueue(deque, nums[i]); 10 } 11 for (int i = k - 1; i < nums.length; i++) { 12 inQueue(deque, nums[i]); 13 result[i - k + 1] = deque.peekFirst(); 14 outQueue(deque, nums[i - k + 1]); 15 } 16 return result; 17 } 18 public void inQueue(Deque<Integer> deque, int num) { 19 while (!deque.isEmpty() && num > deque.peekLast()) { 20 deque.pollLast(); 21 } 22 deque.offerLast(num); 23 } 24 public void outQueue(Deque<Integer> deque, int num) { 25 if (num == deque.peekFirst()) { 26 deque.pollFirst(); 27 } 28 } 29 }
295. Find Median from Data Stream
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value. Examples: [2,3,4] , the median is 3 [2,3], the median is (2 + 3) / 2 = 2.5 Design a data structure that supports the following two operations: void addNum(int num) - Add a integer number from the data stream to the data structure. double findMedian() - Return the median of all elements so far. For example: addNum(1) addNum(2) findMedian() -> 1.5 addNum(3) findMedian() -> 2
思路:维护一个大根堆和一个小根堆,待插入的元素小于等于大根堆堆顶元素值,就插入到大根堆,否则插入到小根堆。每插入一次就要调整堆使大根堆元素个数等于小根堆元素个数或者比小根堆元素个数多1个。调整分两种情况:(1)maxHeap.size() - minHeap.size() > 1 (2)minHeap.size() - maxHeap.size() > 0。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class MedianFinder { 2 PriorityQueue<Integer> maxHeap; 3 PriorityQueue<Integer> minHeap; 4 /** initialize your data structure here. */ 5 public MedianFinder() { 6 maxHeap = new PriorityQueue<>(10, Collections.reverseOrder()); 7 minHeap = new PriorityQueue<>(); 8 } 9 10 public void addNum(int num) { 11 if (maxHeap.isEmpty() || num <= maxHeap.peek()) { 12 maxHeap.offer(num); 13 } else { 14 minHeap.offer(num); 15 } 16 if (maxHeap.size() - minHeap.size() > 1) { 17 minHeap.offer(maxHeap.poll()); 18 } 19 if (minHeap.size() - maxHeap.size() > 0) { 20 maxHeap.offer(minHeap.poll()); 21 } 22 } 23 24 public double findMedian() { 25 int size = maxHeap.size() + minHeap.size(); 26 if (size == 0) { 27 return 0; 28 } 29 if (size % 2 == 0) { 30 return (maxHeap.peek() + minHeap.peek()) / 2.0; 31 } else { 32 return maxHeap.peek(); 33 } 34 } 35 } 36 37 /** 38 * Your MedianFinder object will be instantiated and called as such: 39 * MedianFinder obj = new MedianFinder(); 40 * obj.addNum(num); 41 * double param_2 = obj.findMedian(); 42 */
403. Frog Jump
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
A frog is crossing a river. The river is divided into x units and at each unit there may or may not exist a stone. The frog can jump on a stone, but it must not jump into the water. Given a list of stones' positions (in units) in sorted ascending order, determine if the frog is able to cross the river by landing on the last stone. Initially, the frog is on the first stone and assume the first jump must be 1 unit. If the frog's last jump was k units, then its next jump must be either k - 1, k, or k + 1 units. Note that the frog can only jump in the forward direction. Note: The number of stones is ≥ 2 and is < 1,100. Each stone's position will be a non-negative integer < 231. The first stone's position is always 0. Example 1: [0,1,3,5,6,8,12,17] There are a total of 8 stones. The first stone at the 0th unit, second stone at the 1st unit, third stone at the 3rd unit, and so on... The last stone at the 17th unit. Return true. The frog can jump to the last stone by jumping 1 unit to the 2nd stone, then 2 units to the 3rd stone, then 2 units to the 4th stone, then 3 units to the 6th stone, 4 units to the 7th stone, and 5 units to the 8th stone. Example 2: [0,1,2,3,4,8,9,11] Return false. There is no way to jump to the last stone as the gap between the 5th and 6th stone is too large.
思路:见答案中注释。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean canCross(int[] stones) { 3 if (stones == null || stones.length == 0) { 4 return true; 5 } 6 //1. 状态定义 dp的key表示第i的石头的索引,value表示跳到第i个石头的前一步的所有可能的跳数。 7 Map<Integer, Set<Integer>> map = new HashMap<>(); 8 for (int i = 0; i < stones.length; i++) { 9 map.put(stones[i], new HashSet<>()); 10 } 11 //2. 初始化 12 map.get(0).add(0); 13 //3. 循环递归求解 14 for (int i = 0; i < stones.length; i++) { 15 int stone = stones[i]; 16 for (int steps : map.get(stone)) { 17 if (steps > 1 && map.containsKey(stones[i] + steps - 1)) { 18 map.get(stones[i] + steps - 1).add(steps - 1); 19 } 20 if (steps > 0 && map.containsKey(stones[i] + steps)) { 21 map.get(stones[i] + steps).add(steps); 22 } 23 if (map.containsKey(stones[i] + steps + 1)) { 24 map.get(stones[i] + steps + 1).add(steps + 1); 25 } 26 } 27 } 28 //4. 返回结果 29 return !map.get(stones[stones.length - 1]).isEmpty(); 30 } 31 }
263. Ugly Number
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Write a program to check whether a given number is an ugly number. Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. For example, 6, 8 are ugly while 14 is not ugly since it includes another prime factor 7. Note that 1 is typically treated as an ugly number.
思路:很简单,判断一个数是否为丑数只要不断去掉该数的2、3和5因子,最后判断得到的数是否等于1即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean isUgly(int num) { 3 if (num < 1) { 4 return false; 5 } 6 while (num % 2 == 0) { 7 num /= 2; 8 } 9 while (num % 3 == 0) { 10 num /= 3; 11 } 12 while (num % 5 == 0) { 13 num /= 5; 14 } 15 return num == 1; 16 } 17 }
264. Ugly Number II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Write a program to find the n-th ugly number. Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. For example, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers. Note that 1 is typically treated as an ugly number, and n does not exceed 1690.
思路I:最小堆。每取出一个丑数,将该丑数分别乘以2,3,5得到接下来的丑数,如果堆里面没有就加进去。注意这种方法入堆的丑数总个数大于要求的第n个丑数,因此数据会溢出,所以该用long型最后返回结果时再强制转换回来即可。每次从堆里取出一个最小的丑数,循环n次即可。时间复杂度O(nlogn),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int nthUglyNumber(int n) { 3 if (n < 1) { 4 return 0; 5 } 6 int[] factors = {2, 3, 5}; 7 Queue<Long> queue = new PriorityQueue<>(); 8 queue.offer(1L); 9 long number = 1L; 10 for (int i = 0; i < n; i++) { 11 number = queue.poll(); 12 for (int j = 0; j < 3; j++) { 13 if (!queue.contains(number * factors[j])) { 14 queue.offer(number * factors[j]); 15 } 16 } 17 } 18 return (int) number; 19 } 20 }
思路II:动态规划。新建3个指针分别指向2的倍数、3的倍数和5的倍数的位置,起始都从0开始。每次计算p2 *2, p3 * 3, p5 * 5的最小值作为当前的丑数,当前丑数如果是由p2 * 2得到,则p2++;由p3 * 3得到,则p3++;由p5* 5得到,则p5++。当list中元素个数等于n时停止循环,返回第n个数即可。时间复杂度O(n),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int nthUglyNumber(int n) { 3 if (n < 1) { 4 return 0; 5 } 6 //动态规划 7 List<Integer> result = new ArrayList<>(); 8 result.add(1); 9 int p2 = 0; 10 int p3 = 0; 11 int p5 = 0; 12 while (result.size() < n) { 13 int ugly2 = result.get(p2) * 2; 14 int ugly3 = result.get(p3) * 3; 15 int ugly5 = result.get(p5) * 5; 16 int min = Math.min(ugly2, Math.min(ugly3, ugly5)); 17 result.add(min); 18 if (min == ugly2) { 19 p2++; 20 } 21 if (min == ugly3) { 22 p3++; 23 } 24 if (min == ugly5) { 25 p5++; 26 } 27 } 28 return result.get(n - 1); 29 } 30 }
313. Super Ugly Number
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Write a program to find the nth super ugly number. Super ugly numbers are positive numbers whose all prime factors are in the given prime list primes of size k. For example, [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] is the sequence of the first 12 super ugly numbers given primes = [2, 7, 13, 19] of size 4. Note: (1) 1 is a super ugly number for any given primes. (2) The given numbers in primes are in ascending order. (3) 0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000. (4) The nth super ugly number is guaranteed to fit in a 32-bit signed integer.
思路:同Ugly Number II,但是用最小堆的方法会超时。只能用动态规划方法。注意该题实际上应该是primes数组中不能含有1这个因子,但是允许有重复因子。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int nthSuperUglyNumber(int n, int[] primes) { 3 if (n < 1 || primes == null || primes.length == 0) { 4 return 0; 5 } 6 List<Integer> result = new ArrayList<>(); 7 result.add(1); 8 int[] p = new int[primes.length]; 9 while (result.size() < n) { 10 int min = Integer.MAX_VALUE; 11 for (int i = 0; i < primes.length; i++) { 12 min = Math.min(min, primes[i] * result.get(p[i])); 13 } 14 result.add(min); 15 for (int i = 0; i < primes.length; i++) { 16 if (min == primes[i] * result.get(p[i])) { 17 p[i]++; 18 } 19 } 20 } 21 return result.get(n - 1); 22 } 23 }
373. Find K Pairs with Smallest Sums
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. Define a pair (u,v) which consists of one element from the first array and one element from the second array. Find the k pairs (u1,v1),(u2,v2) ...(uk,vk) with the smallest sums. Example 1: Given nums1 = [1,7,11], nums2 = [2,4,6], k = 3 Return: [1,2],[1,4],[1,6] The first 3 pairs are returned from the sequence: [1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6] Example 2: Given nums1 = [1,1,2], nums2 = [1,2,3], k = 2 Return: [1,1],[1,1] The first 2 pairs are returned from the sequence: [1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3] Example 3: Given nums1 = [1,2], nums2 = [3], k = 3 Return: [1,3],[2,3] All possible pairs are returned from the sequence: [1,3],[2,3]
思路I:维护一个大根堆,容量为k。不需要对两个数组都遍历,只需要遍历两个数组的前k个元素。时间复杂度O(k^2),空间复杂度O(k)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<int[]> kSmallestPairs(int[] nums1, int[] nums2, int k) { 3 List<int[]> result = new ArrayList<>(); 4 if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) { 5 return result; 6 } 7 if (k < 1) { 8 return result; 9 } 10 Queue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() { 11 public int compare(int[] a, int[] b) { 12 return (b[0] + b[1]) - (a[0] + a[1]); 13 } 14 }); 15 for (int i = 0; i < nums1.length && i < k; i++) { 16 for (int j = 0; j < nums2.length && j < k; j++) { 17 if (queue.size() < k) { 18 queue.offer(new int[]{nums1[i], nums2[j]}); 19 } else { 20 int[] temp = queue.peek(); 21 if (nums1[i] + nums2[j] < temp[0] + temp[1]) { 22 queue.poll(); 23 queue.offer(new int[]{nums1[i], nums2[j]}); 24 } 25 } 26 } 27 } 28 while (!queue.isEmpty()) { 29 result.add(queue.poll()); 30 } 31 return result; 32 } 33 }
思路II:维护一个小根堆,先把第一个数组的所有可能与第二个数组中的第一个元素配对,然后每出一个最小值,就新加一对元素进来。时间复杂度O(klogk),由于堆只需要保存nums1.length个数字对,因此空间复杂度为O(nums1.length)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<int[]> kSmallestPairs(int[] nums1, int[] nums2, int k) { 3 List<int[]> result = new ArrayList<>(); 4 if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0 || k < 1) { 5 return result; 6 } 7 Queue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() { 8 @Override 9 public int compare(int[] a, int[] b) { 10 return (a[0] + a[1]) - (b[0] + b[1]); 11 } 12 }); 13 for (int i = 0; i < nums1.length; i++) { 14 queue.offer(new int[]{nums1[i], nums2[0], 0}); 15 } 16 while (k > 0 && !queue.isEmpty()) { 17 int[] temp = queue.poll(); 18 result.add(new int[]{temp[0], temp[1]}); 19 if (temp[2] < nums2.length - 1) { 20 queue.offer(new int[]{temp[0], nums2[temp[2] + 1], temp[2] + 1}); 21 } 22 k--; 23 } 24 return result; 25 } 26 }
385. Mini Parser
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a nested list of integers represented as a string, implement a parser to deserialize it. Each element is either an integer, or a list -- whose elements may also be integers or other lists. Note: You may assume that the string is well-formed: String is non-empty. String does not contain white spaces. String contains only digits 0-9, [, - ,, ]. Example 1: Given s = "324", You should return a NestedInteger object which contains a single integer 324. Example 2: Given s = "[123,[456,[789]]]", Return a NestedInteger object containing a nested list with 2 elements: 1. An integer containing value 123. 2. A nested list containing two elements: i. An integer containing value 456. ii. A nested list with one element: a. An integer containing value 789.
思路:注意栈中元素类型和出入栈的时间节点。合并的次数为'['的个数减1,也就是说循环到字符串倒数第二位,处理完数字的时候索引i = j - 1而不是i!!!因为数字后面既可能是','也可能是']'。注意特殊情况"324"的处理(没有'[')。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * // This is the interface that allows for creating nested lists. 3 * // You should not implement it, or speculate about its implementation 4 * public interface NestedInteger { 5 * // Constructor initializes an empty nested list. 6 * public NestedInteger(); 7 * 8 * // Constructor initializes a single integer. 9 * public NestedInteger(int value); 10 * 11 * // @return true if this NestedInteger holds a single integer, rather than a nested list. 12 * public boolean isInteger(); 13 * 14 * // @return the single integer that this NestedInteger holds, if it holds a single integer 15 * // Return null if this NestedInteger holds a nested list 16 * public Integer getInteger(); 17 * 18 * // Set this NestedInteger to hold a single integer. 19 * public void setInteger(int value); 20 * 21 * // Set this NestedInteger to hold a nested list and adds a nested integer to it. 22 * public void add(NestedInteger ni); 23 * 24 * // @return the nested list that this NestedInteger holds, if it holds a nested list 25 * // Return null if this NestedInteger holds a single integer 26 * public List<NestedInteger> getList(); 27 * } 28 */ 29 public class Solution { 30 public NestedInteger deserialize(String s) { 31 if (s == null || s.length() == 0) { 32 return null; 33 } 34 if (s.charAt(0) != '[') { 35 return new NestedInteger(Integer.parseInt(s)); 36 } 37 Stack<NestedInteger> stack = new Stack<>(); 38 for (int i = 0; i < s.length() - 1; i++) { 39 char c = s.charAt(i); 40 if (c == '[') { 41 stack.push(new NestedInteger()); 42 } else if (c == ']') { 43 NestedInteger ni = stack.pop(); 44 stack.peek().add(ni); 45 } else if (Character.isDigit(c) || c == '-') { 46 int j = i + 1; 47 while (j < s.length() && Character.isDigit(s.charAt(j))) { 48 j++; 49 } 50 String str = s.substring(i, j); 51 stack.peek().add(new NestedInteger(Integer.parseInt(str))); 52 i = j - 1; 53 } 54 } 55 return stack.peek(); 56 } 57 }
349. Intersection of Two Arrays
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two arrays, write a function to compute their intersection. Example: Given nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2]. Note: Each element in the result must be unique. The result can be in any order.
思路I:排序 + 双指针合并,一个指针指向nums1数组索引,一个指针指向nums2数组索引。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int[] intersection(int[] nums1, int[] nums2) { 3 //sort + merge 4 if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) { 5 return new int[0]; 6 } 7 Arrays.sort(nums1); 8 Arrays.sort(nums2); 9 int i = 0; 10 int j = 0; 11 int[] temp = new int[nums1.length]; 12 int index = 0; 13 while (i < nums1.length && j < nums2.length) { 14 if (nums1[i] == nums2[j]) { 15 if (index == 0 || temp[index - 1] != nums1[i]) { 16 temp[index++] = nums1[i]; 17 } 18 i++; 19 j++; 20 } else if (nums1[i] < nums2[j]) { 21 i++; 22 } else { 23 j++; 24 } 25 } 26 int[] result = new int[index]; 27 for (int k = 0; k < index; k++) { 28 result[k] = temp[k]; 29 } 30 return result; 31 } 32 }
思路II:Set + Set,一个set存nums1的元素,另一个set存nums2中在nums1里出现过的元素。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int[] intersection(int[] nums1, int[] nums2) { 3 // Set + Set 4 if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) { 5 return new int[0]; 6 } 7 Set<Integer> set = new HashSet<>(); 8 Set<Integer> resultSet = new HashSet<>(); 9 for (int i = 0; i < nums1.length; i++) { 10 set.add(nums1[i]); 11 } 12 for (int i = 0; i < nums2.length; i++) { 13 if (set.contains(nums2[i]) && !resultSet.contains(nums2[i])) { 14 resultSet.add(nums2[i]); 15 } 16 } 17 int[] result = new int[resultSet.size()]; 18 int index = 0; 19 for (int num : resultSet) { 20 result[index++] = num; 21 } 22 return result; 23 } 24 }
思路III:sort + Binary Search,先讲nums1数组排好序,然后依次便利nums2中的每个元素看是否在nums1中,只有在nums1中并且set中没有此元素才把该元素加入到set中。最后将set复制到新数组返回。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int[] intersection(int[] nums1, int[] nums2) { 3 // Set + Binary Search 4 if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) { 5 return new int[0]; 6 } 7 Arrays.sort(nums1); 8 Set<Integer> set = new HashSet<>(); 9 for (int i = 0; i < nums2.length; i++) { 10 if (binarySearch(nums1, nums2[i]) && !set.contains(nums2[i])) { 11 set.add(nums2[i]); 12 } 13 } 14 int[] result = new int[set.size()]; 15 int index = 0; 16 for (int num : set) { 17 result[index++] = num; 18 } 19 return result; 20 } 21 public boolean binarySearch(int[] nums, int target) { 22 int start = 0; 23 int end = nums.length - 1; 24 while (start + 1 < end) { 25 int mid = start + (end - start) / 2; 26 if (nums[mid] == target) { 27 return true; 28 } else if (nums[mid] < target) { 29 start = mid; 30 } else { 31 end = mid; 32 } 33 } 34 if (nums[start] == target) { 35 return true; 36 } 37 if (nums[end] == target) { 38 return true; 39 } 40 return false; 41 } 42 }
4. Median of Two Sorted Arrays
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)). Example 1: nums1 = [1, 3] nums2 = [2] The median is 2.0 Example 2: nums1 = [1, 2] nums2 = [3, 4] The median is (2 + 3)/2 = 2.5
思路:由于时间复杂度的要求,而且是排序数组,所以使用二分法。问题等价于找合并数组里的第k大的元素。每次从两个数组中取前k / 2个元素,设mid = k / 2,当nums1[mid - 1] < nums2[mid - 1]时,可以断定nums1的前mid个元素在合并数组中的前k个元素里,因此可以扔掉这k / 2个元素,再递归的求剩余两个数组中的第k - k / 2个元素。反之则扔掉nums2的前k / 2个元素。注意点:1. 递归终止条件有三点(1)nums1走到头(2)nums2走到头(3)k == 1。2. 取nums1[mid - 1]和nums2[mid - 1]时,如果发生索引越界,则将该值置为Integer.MAX_VALUE + 1,此时说明一定要扔掉另一个数组的前k / 2个元素。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public double findMedianSortedArrays(int[] nums1, int[] nums2) { 3 if (nums1 == null || nums1.length == 0) { 4 if (nums2 == null || nums2.length == 0) { 5 return 0; 6 } else { 7 int mid = nums2.length / 2; 8 if (nums2.length % 2 == 0) { 9 return (nums2[mid - 1] + nums2[mid]) / 2.0; 10 } else { 11 return nums2[mid]; 12 } 13 } 14 } 15 if (nums2 == null || nums2.length == 0) { 16 int mid = nums1.length / 2; 17 if (nums1.length % 2 == 0) { 18 return (nums1[mid - 1] + nums1[mid]) / 2.0; 19 } else { 20 return nums1[mid]; 21 } 22 } 23 int length = nums1.length + nums2.length; 24 if (length % 2 == 0) { 25 return (findKth(nums1, 0, nums2, 0, length / 2) + findKth(nums1, 0, nums2, 0, length / 2 + 1)) / 2.0; 26 } else { 27 return findKth(nums1, 0, nums2, 0, length / 2 + 1); 28 } 29 } 30 public int findKth(int[] nums1, int nums1_start, int[] nums2, int nums2_start, int k) { 31 if (nums1_start == nums1.length) { 32 return nums2[nums2_start + k - 1]; 33 } 34 if (nums2_start == nums2.length) { 35 return nums1[nums1_start + k - 1]; 36 } 37 if (k == 1) { 38 return Math.min(nums1[nums1_start], nums2[nums2_start]); 39 } 40 long nums1_key = nums1_start + k / 2 - 1 < nums1.length ? nums1[nums1_start + k / 2 - 1] : (long) Integer.MAX_VALUE + 1; 41 long nums2_key = nums2_start + k / 2 - 1 < nums2.length ? nums2[nums2_start + k / 2 - 1] : (long) Integer.MAX_VALUE + 1; 42 if (nums1_key < nums2_key) { 43 return findKth(nums1, nums1_start + k / 2, nums2, nums2_start, k - k / 2); 44 } else { 45 return findKth(nums1, nums1_start, nums2, nums2_start + k / 2, k - k / 2); 46 } 47 } 48 }
209. Minimum Size Subarray Sum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn't one, return 0 instead. For example, given the array [2,3,1,2,4,3] and s = 7, the subarray [4,3] has the minimal length under the problem constraint.
思路:两根指针,维持一个动态窗口。前面指针移动直到满足条件停止,然后后面指针向前移,前面指针接着进行下一次移动。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int minSubArrayLen(int s, int[] nums) { 3 if (nums == null || nums.length == 0) { 4 return 0; 5 } 6 int i = 0; 7 int j = 0; 8 int sum = 0; 9 int min = Integer.MAX_VALUE; 10 for (i = 0; i < nums.length; i++) { 11 while (j < nums.length && sum < s) { 12 sum += nums[j]; 13 j++; 14 } 15 if (sum >= s) { 16 min = Math.min(min, j - i); 17 } 18 sum -= nums[i]; 19 } 20 if (min == Integer.MAX_VALUE) { 21 return 0; 22 } 23 return min; 24 } 25 }
类似题 --
3. Longest Substring Without Repeating Characters
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a string, find the length of the longest substring without repeating characters. Examples: Given "abcabcbb", the answer is "abc", which the length is 3. Given "bbbbb", the answer is "b", with the length of 1. Given "pwwkew", the answer is "wke", with the length of 3. Note that the answer must be a substring, "pwke" is a subsequence and not a substring.
思路:两根指针,同时用Set存放不含重复元素的子串的字符。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int lengthOfLongestSubstring(String s) { 3 if (s == null || s.length() == 0) { 4 return 0; 5 } 6 int i = 0; 7 int j = 0; 8 int max = Integer.MIN_VALUE; 9 Set<Character> set = new HashSet<>(); 10 for (i = 0; i < s.length(); i++) { 11 while (j < s.length() && !set.contains(s.charAt(j))) { 12 set.add(s.charAt(j)); 13 j++; 14 } 15 max = Math.max(max, set.size()); 16 set.remove(s.charAt(i)); 17 } 18 return max; 19 } 20 }
类似题 --
76. Minimum Window Substring
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n). For example, S = "ADOBECODEBANC" T = "ABC" Minimum window is "BANC". Note: If there is no such window in S that covers all characters in T, return the empty string "". If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.
思路:两根指针,用长度为256的数组存放字符串中每个字符出现的次数,索引是字符的ASCII码。这样方面窗口滑动的时候进行是否包含字符串的比较和移除字符的操作。有个技巧:判断一个字符串是否包含有另一个字符串,可以将这两个字符串转换成为两个长度为256的数组,索引是字符的ASCII码(0 - 255),里面的值是字符出现的次数,然后对前面一个字符串依次遍历,只要碰到数组元素值小于另一个数组相对应的元素值返回false,遍历完也没有返回false就返回true。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public String minWindow(String s, String t) { 3 if (s == null || s.length() == 0 || t == null || t.length() == 0) { 4 return ""; 5 } 6 int[] targetHash = initialize(t); 7 int[] sourceHash = new int[256]; 8 int i = 0; 9 int j = 0; 10 int minLength = Integer.MAX_VALUE; 11 String minString = ""; 12 for (i = 0; i < s.length(); i++) { 13 while (j < s.length() && !valid(sourceHash, targetHash)) { 14 sourceHash[s.charAt(j)]++; 15 j++; 16 } 17 if (valid(sourceHash, targetHash)) { 18 if (j - i < minLength) { 19 minLength = j - i; 20 minString = s.substring(i, j); 21 } 22 } 23 sourceHash[s.charAt(i)]--; 24 } 25 return minString; 26 } 27 public int[] initialize(String t) { 28 int[] targetHash = new int[256]; 29 for (int i = 0; i < t.length(); i++) { 30 targetHash[t.charAt(i)]++; 31 } 32 return targetHash; 33 } 34 public boolean valid(int[] sourceHash, int[] targetHash) { 35 for (int i = 0; i < sourceHash.length; i++) { 36 if (sourceHash[i] < targetHash[i]) { 37 return false; 38 } 39 } 40 return true; 41 } 42 }
类似题 -- Longest Substring with At Most K Distinct Characters
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a string s, find the length of the longest substring T that contains at most k distinct characters. Example For example, Given s = "eceba", k = 3, T is "eceb" which its length is 4.
思路:用Map存放每个字符出现的次数,不能用Set因为需要知道字符出现的次数以判断当前窗口中不同字符的个数。注意更新maxLength时对应的两种情况,(1)出while循环后map的size大于k,那么取j - i - 1;(2)出while循环后map的size小于等于k,说明此时走到头了,那么取j - i。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param s : A string 4 * @return : The length of the longest substring 5 * that contains at most k distinct characters. 6 */ 7 public int lengthOfLongestSubstringKDistinct(String s, int k) { 8 // write your code here 9 if (s == null || s.length() == 0 || k < 1) { 10 return 0; 11 } 12 int i = 0; 13 int j = 0; 14 int maxLength = 0; 15 Map<Character, Integer> map = new HashMap<>(); 16 for (i = 0; i < s.length(); i++) { 17 while (j < s.length() && map.size() <= k) { 18 char c = s.charAt(j); 19 if (!map.containsKey(c)) { 20 map.put(c, 1); 21 } else { 22 map.put(c, map.get(c) + 1); 23 } 24 j++; 25 } 26 if (map.size() > k) { 27 maxLength = Math.max(maxLength, j - i - 1); 28 } else { 29 maxLength = Math.max(maxLength, j - i); 30 } 31 char temp = s.charAt(i); 32 if (map.get(temp) > 1) { 33 map.put(temp, map.get(temp) - 1); 34 } else { 35 map.remove(temp); 36 } 37 } 38 return maxLength; 39 } 40 }
378. Kth Smallest Element in a Sorted Matrix
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in the matrix. Note that it is the kth smallest element in the sorted order, not the kth distinct element. Example: matrix = [ [ 1, 5, 9], [10, 11, 13], [12, 13, 15] ], k = 8, return 13. Note: You may assume k is always valid, 1 ≤ k ≤ n2.
思路:维护一个最小堆,向右倾斜45度遍历元素入堆,遍历k - 1次出堆前k - 1小的元素,然后堆顶元素即为答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Element { 2 int x; 3 int y; 4 int val; 5 public Element(int x, int y, int val) { 6 this.x = x; 7 this.y = y; 8 this.val = val; 9 } 10 } 11 public class Solution { 12 public int kthSmallest(int[][] matrix, int k) { 13 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 14 return 0; 15 } 16 int n = matrix.length; 17 int m = matrix[0].length; 18 if (k < 1 || k > n * m) { 19 return 0; 20 } 21 PriorityQueue<Element> pq = new PriorityQueue<Element>(k, new Comparator<Element>() { 22 public int compare(Element a, Element b) { 23 return a.val - b.val; 24 } 25 }); 26 boolean[][] visited = new boolean[n][m]; 27 pq.offer(new Element(0, 0, matrix[0][0])); 28 visited[0][0] = true; 29 int[] deltaX = {1, 0}; 30 int[] deltaY = {0, 1}; 31 for (int i = 0; i < k - 1; i++) { 32 Element element = pq.poll(); 33 for (int j = 0; j < 2; j++) { 34 int next_x = element.x + deltaX[j]; 35 int next_y = element.y + deltaY[j]; 36 if (next_x < n && next_y < m && !visited[next_x][next_y]) { 37 pq.offer(new Element(next_x, next_y, matrix[next_x][next_y])); 38 visited[next_x][next_y] = true; 39 } 40 } 41 } 42 return pq.poll().val; 43 } 44 }
Kth Largest in N Arrays
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Find K-th largest element in N arrays. You can swap elements in the array Example In n=2 arrays [[9,3,2,4,7],[1,2,3,4,8]], the 3rd largest element is 7. In n=2 arrays [[9,3,2,4,8],[1,2,3,4,2]], the 1st largest element is 9, 2nd largest element is 8, 3rd largest element is 7 and etc.
思路:这道题细节地方很多,把二维数组每一行都从小到大排好序,然后把每一行的最后面一个元素入大根堆,接着依次出堆k - 1次,每次把取出的元素的前一个位置的元素[i, j - 1]入堆。最后返回堆顶元素即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 int val; 5 public Node(int x, int y, int val) { 6 this.x = x; 7 this.y = y; 8 this.val = val; 9 } 10 } 11 public class Solution { 12 /** 13 * @param arrays a list of array 14 * @param k an integer 15 * @return an integer, K-th largest element in N arrays 16 */ 17 public int KthInArrays(int[][] arrays, int k) { 18 // Write your code here 19 if (arrays == null || arrays.length == 0) { 20 return 0; 21 } 22 int n = arrays.length; 23 if (k < 1) { 24 return 0; 25 } 26 PriorityQueue<Node> pq = new PriorityQueue<>(k, new Comparator<Node>() { 27 public int compare(Node a, Node b) { 28 return b.val - a.val; 29 } 30 }); 31 int sum = 0; 32 for (int i = 0; i < n; i++) { 33 Arrays.sort(arrays[i]); 34 int len = arrays[i].length; 35 sum += len; 36 if (len != 0) { 37 pq.offer(new Node(i, len - 1, arrays[i][len - 1])); 38 } 39 } 40 if (k > sum) { 41 return 0; 42 } 43 for (int i = 0; i < k - 1; i++) { 44 Node node = pq.poll(); 45 if (node.y - 1 >= 0) { 46 pq.offer(new Node(node.x, node.y - 1, 47 arrays[node.x][node.y - 1])); 48 } 49 } 50 return pq.poll().val; 51 } 52 }
Kth Smallest Sum in Two Sorted Arrays
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two integer arrays sorted in ascending order and an integer k. Define sum = a + b, where a is an element from the first array and b is an element from the second one. Find the kth smallest sum out of all possible sums. Example Given [1, 7, 11] and [2, 4, 6]. For k = 3, return 7. For k = 4, return 9. For k = 8, return 15.
思路:小根堆,每次只需要判断i+1,j和i,j+1这两个位置上(如果位置没超出限制的话)的元素是否需要加入堆,用一个visited数组避免相同元素重重复入堆的问题。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int index1; 3 int index2; 4 int sum; 5 public Node(int index1, int index2, int sum) { 6 this.index1 = index1; 7 this.index2 = index2; 8 this.sum = sum; 9 } 10 } 11 public class Solution { 12 /** 13 * @param A an integer arrays sorted in ascending order 14 * @param B an integer arrays sorted in ascending order 15 * @param k an integer 16 * @return an integer 17 */ 18 public int kthSmallestSum(int[] A, int[] B, int k) { 19 // Write your code here 20 if (A == null || A.length == 0 || B == null || B.length == 0) { 21 return 0; 22 } 23 Queue<Node> queue = new PriorityQueue<Node>(k, new Comparator<Node>() { 24 public int compare(Node a, Node b) { 25 return a.sum - b.sum; 26 } 27 }); 28 boolean[][] visited = new boolean[A.length][B.length]; 29 queue.offer(new Node(0, 0, A[0] + B[0])); 30 visited[0][0] = true; 31 for (int i = 0; i < k - 1; i++) { 32 Node node = queue.poll(); 33 if (node.index1 + 1 < A.length 34 && !visited[node.index1 + 1][node.index2]) { 35 int sum = A[node.index1 + 1] + B[node.index2]; 36 queue.offer(new Node(node.index1 + 1, node.index2, sum)); 37 visited[node.index1 + 1][node.index2] = true; 38 } 39 if (node.index2 + 1 < B.length 40 && !visited[node.index1][node.index2 + 1]) { 41 int sum = A[node.index1] + B[node.index2 + 1]; 42 queue.offer(new Node(node.index1, node.index2 + 1, sum)); 43 visited[node.index1][node.index2 + 1] = true; 44 } 45 } 46 return queue.peek().sum; 47 } 48 }
128. Longest Consecutive Sequence
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an unsorted array of integers, find the length of the longest consecutive elements sequence. For example, Given [100, 4, 200, 1, 3, 2], The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4. Your algorithm should run in O(n) complexity.
思路:用一个set装入数组中不重复的元素,然后对数组中的每一个元素,在set找以该元素为中间节点,连续递减的元素个数和连续递增的元素个数,每找到一个就把该元素从set里面删除,以免后来重复遍历。最后取连续个数的最小值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int longestConsecutive(int[] nums) { 3 Set<Integer> set = new HashSet<>(); 4 for (int i = 0; i < nums.length; i++) { 5 set.add(nums[i]); 6 } 7 int longest = 0; 8 for (int i = 0; i < nums.length; i++) { 9 set.remove(nums[i]); 10 11 int down = nums[i] - 1; 12 while (set.contains(down)) { 13 set.remove(down); 14 down--; 15 } 16 17 int up = nums[i] + 1; 18 while (set.contains(up)) { 19 set.remove(up); 20 up++; 21 } 22 23 longest = Math.max(longest, up - down - 1); 24 } 25 return longest; 26 } 27 }
421. Maximum XOR of Two Numbers in an Array
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 231. Find the maximum result of ai XOR aj, where 0 ≤ i, j < n. Could you do this in O(n) runtime? Example: Input: [3, 10, 5, 25, 2, 8] Output: 28 Explanation: The maximum result is 5 ^ 25 = 28.
思路I:Bit Manipulation + HashSet
每次从高位开始,到某一位为止,所能获得的最大的数。设置变量mask用来表示能形成的值,每一次将mask和其他的num相与得到的值加入set,表示在当前这一位上,数组里有这么多prefix。
假定在某一位上的任意两数xor能得到的最大值是dummyMax,那么他一定满足a(xor)b = dummyMax,其中set.contains(a) && set.contains(b). 利用XOR的性质,a ^ b = c, 则有a ^ c = b,且b ^ c = a,我们只需要判断b(xor)tmp的结果是不是在当前这一位下的set内,就可以知道这个dummyMax能不能由这个set中的任意两个数组成。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int findMaximumXOR(int[] nums) { 3 if (nums == null || nums.length <= 1) { 4 return 0; 5 } 6 int mask = 0; 7 int max = 0; 8 for (int i = 31; i >= 0; i--) { 9 mask = mask | (1 << i); 10 Set<Integer> set = new HashSet<>(); 11 for (int num : nums) { 12 set.add(mask & num); 13 } 14 // 假设当前异或结果所能达到的最大值为dummyMax 15 int dummyMax = max | (1 << i); 16 for (int prefix : set) { 17 // 利用了XOR的性质,a ^ b = c, 则有a ^ c = b,且b ^ c = a 18 if (set.contains(dummyMax ^ prefix)) { 19 max = dummyMax; 20 break; 21 } 22 } 23 } 24 return max; 25 } 26 }
思路II:Trie, 但是对于这道题有一个大样例过不了,不科学!
建立字典树,将数组中的每个数的二进制表示的每一位都存储到字典树中。然后对数组中的每个数,采用类似贪心的策略,从最高位开始,在字典树中找和它在这一位相反的数。如果有,那么和这个数异或就得到最大异或值,如果没有就依次往下一位找,直到找到相异的位。每遍历完一个数,就得到一个异或值,最后遍历完数组中所有的数就可以得到最大的异或值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class TrieNode { 2 Map<Integer, TrieNode> children; 3 public TrieNode() { 4 children = new HashMap<>(); 5 } 6 } 7 class Trie { 8 TrieNode root; 9 public Trie() { 10 root = new TrieNode(); 11 } 12 public void insert(int num) { 13 TrieNode cur = root; 14 for (int i = 31; i >= 0; i--) { 15 int bit = (num >> i) & 1; 16 if (!cur.children.containsKey(bit)) { 17 cur.children.put(bit, new TrieNode()); 18 } 19 cur = cur.children.get(bit); 20 } 21 } 22 } 23 public class Solution { 24 public int findMaximumXOR(int[] nums) { 25 if (nums == null || nums.length <= 1) { 26 return 0; 27 } 28 Trie trie = new Trie(); 29 for (int num : nums) { 30 trie.insert(num); 31 } 32 int max = 0; 33 for (int num : nums) { 34 int xor = 0; 35 TrieNode cur = trie.root; 36 for (int i = 31; i >= 0; i--) { 37 int bit = (num >> i) & 1; 38 if (cur.children.containsKey(1 - bit)) { 39 cur = cur.children.get(1 - bit); 40 xor += 1 << i; 41 } else { 42 cur = cur.children.get(bit); 43 } 44 if (xor + (1 << i) - 1 <= max) { 45 break; 46 } 47 } 48 max = Math.max(max, xor); 49 } 50 return max; 51 } 52 }
5. Longest Palindromic Substring
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000. Example: Input: "babad" Output: "bab" Note: "aba" is also a valid answer. Example: Input: "cbbd" Output: "bb"
思路:动态规划。dp[i][j]表示i ~ j子串是否是回文串。如果s.charAt(i) != s.charAt(j),dp[i][j] = false;如果s.charAt(i) == s.charAt(j),dp[i][j] = dp[i + 1][j - 1]。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public String longestPalindrome(String s) { 3 if (s == null || s.length() == 0) { 4 return ""; 5 } 6 if (s.length() == 1) { 7 return s; 8 } 9 int maxLength = 1; 10 int left = 0; 11 int right = 0; 12 int n = s.length(); 13 boolean[][] dp = new boolean[n][n]; 14 for (int i = 0; i < n; i++) { 15 dp[i][i] = true; 16 if (i + 1 < n) { 17 dp[i][i + 1] = s.charAt(i) == s.charAt(i + 1); 18 if (dp[i][i + 1] && maxLength < 2) { 19 maxLength = 2; 20 left = i; 21 right = i + 1; 22 } 23 } 24 } 25 for (int len = n - 2; len >= 1; len--) { 26 for (int i = 0, j = n - len; i < len; i++, j++) { 27 if (s.charAt(i) != s.charAt(j)) { 28 dp[i][j] = false; 29 } else { 30 dp[i][j] = dp[i + 1][j - 1]; 31 if (dp[i][j] && j - i + 1 > maxLength) { 32 maxLength = j - i + 1; 33 left = i; 34 right = j; 35 } 36 } 37 } 38 } 39 return s.substring(left, right + 1); 40 } 41 }
87. Scramble String
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively. Below is one possible representation of s1 = "great": great / \ gr eat / \ / \ g r e at / \ a t To scramble the string, we may choose any non-leaf node and swap its two children. For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat". rgeat / \ rg eat / \ / \ r g e at / \ a t We say that "rgeat" is a scrambled string of "great". Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae". rgtae / \ rg tae / \ / \ r g ta e / \ t a We say that "rgtae" is a scrambled string of "great". Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.
思路I:DFS 简单的说,就是s1和s2是scramble的话,那么必然存在一个在s1上的长度l1,将s1分成s11和s12两段,同样有s21和s22.那么要么s11和s21是scramble的并且s12和s22是scramble的;要么s11和s22是scramble的并且s12和s21是scramble的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param s1 A string 4 * @param s2 Another string 5 * @return whether s2 is a scrambled string of s1 6 */ 7 public boolean isScramble(String s1, String s2) { 8 // Write your code here 9 if (s1 == null && s2 == null) { 10 return true; 11 } 12 if (s1 == null && s2 != null || s1 != null && s2 == null) { 13 return false; 14 } 15 if (s1.length() != s2.length()) { 16 return false; 17 } 18 if (s1.equals(s2)) { 19 return true; 20 } 21 char[] str1 = s1.toCharArray(); 22 char[] str2 = s2.toCharArray(); 23 Arrays.sort(str1); 24 Arrays.sort(str2); 25 for (int i = 0; i < s1.length(); i++) { 26 if (str1[i] != str2[i]) { 27 return false; 28 } 29 } 30 for (int i = 1; i < s1.length(); i++) { 31 String s11 = s1.substring(0, i); 32 String s12 = s1.substring(i, s1.length()); 33 String s21 = s2.substring(0, i); 34 String s22 = s2.substring(i, s1.length()); 35 if (isScramble(s11, s21) && isScramble(s12, s22)) { 36 return true; 37 } 38 s21 = s2.substring(s1.length() - i); 39 s22 = s2.substring(0, s1.length() - i); 40 if (isScramble(s11, s21) && isScramble(s12, s22)) { 41 return true; 42 } 43 } 44 return false; 45 } 46 }
思路II:DP 跟字符串有关的题十有八九可以用DP来做,那么难点就在于如何找出递推公式。这其实是一道三维动态规划的题目,我们提出维护量res[i][j][n],其中i是s1的起始字符,j是s2的起始字符,而n是当前的字符串长度,res[i][j][len]表示的是以i和j分别为s1和s2起点的长度为len的字符串是不是互为scramble。
有了维护量我们接下来看看递推式,也就是怎么根据历史信息来得到res[i][j][len]。判断这个是不是满足,其实我们首先是把当前s1[i...i+len-1]字符串劈一刀分成两部分,然后分两种情况:第一种是左边和s2[j...j+len-1]左边部分是不是scramble,以及右边和s2[j...j+len-1]右边部分是不是scramble;第二种情况是左边和s2[j...j+len-1]右边部分是不是scramble,以及右边和s2[j...j+len-1]左边部分是不是scramble。如果以上两种情况有一种成立,说明s1[i...i+len-1]和s2[j...j+len-1]是scramble的。而对于判断这些左右部分是不是scramble我们是有历史信息的,因为长度小于n的所有情况我们都在前面求解过了(也就是长度是最外层循环)。
上面说的是劈一刀的情况,对于s1[i...i+len-1]我们有len-1种劈法,在这些劈法中只要有一种成立,那么两个串就是scramble的。
总结起来递推式是res[i][j][len] = || (res[i][j][k]&&res[i+k][j+k][len-k] || res[i][j+len-k][k]&&res[i+k][j][len-k]) 对于所有1<=k<len,也就是对于所有len-1种劈法的结果求或运算。因为信息都是计算过的,对于每种劈法只需要常量操作即可完成,因此求解递推式是需要O(len)(因为len-1种劈法)。
如此总时间复杂度因为是三维动态规划,需要三层循环,加上每一步需要线行时间求解递推式,所以是O(n^4)。虽然已经比较高了,但是至少不是指数量级的,动态规划还是有很大优势的,空间复杂度是O(n^3)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param s1 A string 4 * @param s2 Another string 5 * @return whether s2 is a scrambled string of s1 6 */ 7 public boolean isScramble(String s1, String s2) { 8 // Write your code here 9 if (s1 == null && s2 == null) { 10 return true; 11 } 12 if (s1 == null && s2 != null || s1 != null && s2 == null) { 13 return false; 14 } 15 if (s1.length() != s2.length()) { 16 return false; 17 } 18 int n = s1.length(); 19 boolean[][][] dp = new boolean[n][n][n + 1]; 20 for (int i = 0; i < n; i++) { 21 for (int j = 0; j < n; j++) { 22 dp[i][j][1] = s1.charAt(i) == s2.charAt(j); 23 } 24 } 25 for (int len = 2; len <= n; len++) { 26 for (int i = 0; i <= n - len; i++) { 27 for (int j = 0; j <= n - len; j++) { 28 for (int k = 1; k <= len - 1; k++) { 29 if (dp[i][j][k] && dp[i + k][j + k][len - k] 30 || dp[i][j + len - k][k] && dp[i + k][j][len - k]) { 31 dp[i][j][len] = true; 32 } 33 } 34 } 35 } 36 } 37 return dp[0][0][n]; 38 } 39 }
lintcode类似题
Longest Common Subsequence
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two strings, find the longest common subsequence (LCS). Your code should return the length of LCS. Have you met this question in a real interview? Yes Clarification What's the definition of Longest Common Subsequence? https://en.wikipedia.org/wiki/Longest_common_subsequence_problem http://baike.baidu.com/view/2020307.htm Example For "ABCD" and "EDCA", the LCS is "A" (or "D", "C"), return 1. For "ABCD" and "EACB", the LCS is "AC", return 2.
思路:DP dp[i][j]表示A串的前i个字符与B串的前j个字符的LCS个数。如果A.charAt(i - 1) == B.charAt(j - 1), dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1] + 1);如果A.charAt(i - 1) != B.charAt(j - 1),dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])。返回dp[n][m]。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param A, B: Two strings. 4 * @return: The length of longest common subsequence of A and B. 5 */ 6 public int longestCommonSubsequence(String A, String B) { 7 // write your code here 8 if (A == null || B == null) { 9 return 0; 10 } 11 int n = A.length(); 12 int m = B.length(); 13 int[][] dp = new int[n + 1][m + 1]; 14 for (int i = 1; i <= n; i++) { 15 for (int j = 1; j <= m; j++) { 16 if (A.charAt(i - 1) == B.charAt(j - 1)) { 17 dp[i][j] = Math.max(Math.max(dp[i - 1][j], 18 dp[i][j - 1]), dp[i - 1][j - 1] + 1); 19 } else { 20 dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); 21 } 22 } 23 } 24 return dp[n][m]; 25 } 26 }
Longest Common Substring
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two strings, find the longest common substring. Return the length of it. Notice The characters in substring should occur continuously in original string. This is different with subsequence. Have you met this question in a real interview? Yes Example Given A = "ABCD", B = "CBCE", return 2.
思路:dp[i][j]表示以A串中第i个字符串结尾和以B串中第j个字符串结尾的LCS个数。如果A.charAt(i - 1) == B.charAt(j - 1),dp[i][j] = dp[i - 1][j - 1] + 1;如果A.charAt(i - 1) != B.charAt(j - 1),dp[i][j] = 1。返回dp[i][j]的最大值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param A, B: Two string. 4 * @return: the length of the longest common substring. 5 */ 6 public int longestCommonSubstring(String A, String B) { 7 // write your code here 8 if (A == null || B == null) { 9 return 0; 10 } 11 int n = A.length(); 12 int m = B.length(); 13 int[][] dp = new int[n + 1][m + 1]; 14 int max = 0; 15 for (int i = 1; i <= n; i++) { 16 for (int j = 1; j <= m; j++) { 17 if (A.charAt(i - 1) == B.charAt(j - 1)) { 18 dp[i][j] = dp[i - 1][j - 1] + 1; 19 } else { 20 dp[i][j] = 0; 21 } 22 max = Math.max(max, dp[i][j]); 23 } 24 } 25 return max; 26 } 27 }
475. Heaters
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Winter is coming! Your first job during the contest is to design a standard heater with fixed warm radius to warm all the houses. Now, you are given positions of houses and heaters on a horizontal line, find out minimum radius of heaters so that all houses could be covered by those heaters. So, your input will be the positions of houses and heaters seperately, and your expected output will be the minimum radius standard of heaters. Note: Numbers of houses and heaters you are given are non-negative and will not exceed 25000. Positions of houses and heaters you are given are non-negative and will not exceed 10^9. As long as a house is in the heaters' warm radius range, it can be warmed. All the heaters follow your radius standard and the warm radius will the same. Example 1: Input: [1,2,3],[2] Output: 1 Explanation: The only heater was placed in the position 2, and if we use the radius 1 standard, then all the houses can be warmed. Example 2: Input: [1,2,3,4],[1,4] Output: 1 Explanation: The two heater was placed in the position 1 and 4. We need to use radius 1 standard, then all the houses can be warmed.
思路:二分,对于每个房子的位置,在排好序的heaters中找到该房子的位置或者应该插入的位置(二分来做),然后求该位置与其左右最相邻的两个房子的距离的最小值(注意边界情况的处理,比如左边没有房子或者右边没有房子,相应的距离就置为Integer.MAX_VALUE),遍历每个房子的位置,找出这些每次得到的最小值中的最大值即为所求。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int findRadius(int[] houses, int[] heaters) { 3 if (houses == null || houses.length == 0 || heaters == null || heaters.length == 0) { 4 return -1; 5 } 6 Arrays.sort(heaters); 7 int result = 0; 8 for (int house : houses) { 9 int index = binarySearch(heaters, house); 10 //System.out.println(index); 11 int dist1 = index - 1 >= 0 ? house - heaters[index - 1] : Integer.MAX_VALUE; 12 int dist2 = index < heaters.length ? heaters[index] - house : Integer.MAX_VALUE; 13 result = Math.max(result, Math.min(dist1, dist2)); 14 } 15 return result; 16 } 17 public int binarySearch(int[] heaters, int target) { 18 int start = 0; 19 int end = heaters.length - 1; 20 while (start + 1 < end) { 21 int mid = start + (end - start) / 2; 22 if (heaters[mid] <= target) { 23 start = mid; 24 } else { 25 end = mid; 26 } 27 } 28 if (heaters[start] > target) { 29 return start; 30 } 31 if (heaters[start] == target) { 32 return start; 33 } 34 if (heaters[end] >= target) { 35 return end; 36 } 37 return end + 1; 38 } 39 }
287. Find the Duplicate Number
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one. Note: You must not modify the array (assume the array is read only). You must use only constant, O(1) extra space. Your runtime complexity should be less than O(n2). There is only one duplicate number in the array, but it could be repeated more than once.
思路:二分答案,重复的数范围在1 ~ n之间。对于每一个二分的数,计算数组中小于等于这个二分数的个数,如果小于等于这个二分数,说明重复数字在右半区域;否则在左半区域。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int findDuplicate(int[] nums) { 3 if (nums == null || nums.length < 2) { 4 return 0; 5 } 6 int start = 1; 7 int end = nums.length - 1; 8 while (start + 1 < end) { 9 int mid = start + (end - start) / 2; 10 if (count(nums, mid) <= mid) { 11 start = mid; 12 } else { 13 end = mid; 14 } 15 } 16 if (count(nums, start) > start) { 17 return start; 18 } 19 return end; 20 } 21 public int count(int[] nums, int value) { 22 int result = 0; 23 for (int i = 0; i < nums.length; i++) { 24 if (nums[i] <= value) { 25 result++; 26 } 27 } 28 return result; 29 } 30 }
85. Maximal Rectangle
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area. For example, given the following matrix: 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 Return 6.
思路:单调栈 二维数组从上到下累计直方图的高度,每一行看作是直方图的宽度。然后转化为求每一行的直方图能构成的最大面积即转化成Largest Rectangle in Histogram问题。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maximalRectangle(char[][] matrix) { 3 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 4 return 0; 5 } 6 int n = matrix.length; 7 int m = matrix[0].length; 8 int[][] height = new int[n][m]; 9 for (int i = 0; i < m; i++) { 10 if (matrix[0][i] == '0') { 11 height[0][i] = 0; 12 } else { 13 height[0][i] = 1; 14 } 15 } 16 for (int i = 1; i < n; i++) { 17 for (int j = 0; j < m; j++) { 18 if (matrix[i][j] == '0') { 19 height[i][j] = 0; 20 } else { 21 height[i][j] = height[i - 1][j] + 1; 22 } 23 } 24 } 25 int result = 0; 26 for (int i = 0; i < n; i++) { 27 Stack<Integer> stack = new Stack<>(); 28 for (int j = 0; j <= m; j++) { 29 int cur = j == m ? 0 : height[i][j]; 30 while (!stack.isEmpty() && cur <= height[i][stack.peek()]) { 31 int h = height[i][stack.pop()]; 32 int w = stack.isEmpty() ? j : j - stack.peek() - 1; 33 result = Math.max(result, h * w); 34 } 35 stack.push(j); 36 } 37 } 38 return result; 39 } 40 }
72. Edit Distance
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.) You have the following 3 operations permitted on a word: a) Insert a character b) Delete a character c) Replace a character
思路:1.状态 dp[i][j]表示A的前i个字符最少要用几次编辑可以变成B的前j个字符
2.方程 A[i - 1] == B[j - 1],dp[i][i] = dp[i - 1][j - 1];A[i - 1] != B[j - 1],dp[i][j] = 1 + Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1])。
3.初始化 dp[i][0] = i dp[0][j] = j
4.答案 dp[n][m]
5. 可以用滚动数组优化空间复杂度
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int minDistance(String word1, String word2) { 3 if (word1 == null || word2 == null) { 4 return 0; 5 } 6 int n = word1.length(); 7 int m = word2.length(); 8 int[][] dp = new int[2][m + 1]; 9 for (int j = 0; j <= m; j++) { 10 dp[0][j] = j; 11 } 12 for (int i = 1; i <= n; i++) { 13 for (int j = 0; j <= m; j++) { 14 if (j == 0) { 15 dp[i % 2][j] = i; 16 continue; 17 } 18 if (word1.charAt(i - 1) == word2.charAt(j - 1)) { 19 dp[i % 2][j] = dp[(i - 1) % 2][j - 1]; 20 } else { 21 dp[i % 2][j] = Math.min(dp[(i - 1) % 2][j - 1], Math.min(dp[(i - 1) % 2][j], dp[i % 2][j - 1])) + 1; 22 } 23 } 24 } 25 return dp[n % 2][m]; 26 } 27 }
566. Reshape the Matrix
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
In MATLAB, there is a very useful function called 'reshape', which can reshape a matrix into a new one with different size but keep its original data. You're given a matrix represented by a two-dimensional array, and two positive integers r and c representing the row number and column number of the wanted reshaped matrix, respectively. The reshaped matrix need to be filled with all the elements of the original matrix in the same row-traversing order as they were. If the 'reshape' operation with given parameters is possible and legal, output the new reshaped matrix; Otherwise, output the original matrix. Example 1: Input: nums = [[1,2], [3,4]] r = 1, c = 4 Output: [[1,2,3,4]] Explanation: The row-traversing of nums is [1,2,3,4]. The new reshaped matrix is a 1 * 4 matrix, fill it row by row by using the previous list. Example 2: Input: nums = [[1,2], [3,4]] r = 2, c = 4 Output: [[1,2], [3,4]] Explanation: There is no way to reshape a 2 * 2 matrix to a 2 * 4 matrix. So output the original matrix. Note: The height and width of the given matrix is in range [1, 100]. The given r and c are all positive.
思路:简单题,依次复制就好,复制到最大列的时候把行数加1,列数置0即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int[][] matrixReshape(int[][] nums, int r, int c) { 3 if (nums == null || nums.length == 0 || nums[0].length == 0) { 4 return new int[0][0]; 5 } 6 int n = nums.length; 7 int m = nums[0].length; 8 if (r <= 0 || c <= 0 || n * m != r * c) { 9 return nums; 10 } 11 int[][] result = new int[r][c]; 12 int row = 0; 13 int column = 0; 14 for (int i = 0; i < n; i++) { 15 for (int j = 0; j < m; j++) { 16 result[row][column++] = nums[i][j]; 17 if (column == c) { 18 row++; 19 column = 0; 20 } 21 } 22 } 23 return result; 24 } 25 }
561. Array Partition I
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of 2n integers, your task is to group these integers into n pairs of integer, say (a1, b1), (a2, b2), ..., (an, bn) which makes sum of min(ai, bi) for all i from 1 to n as large as possible. Example 1: Input: [1,4,3,2] Output: 4 Explanation: n is 2, and the maximum sum of pairs is 4. Note: n is a positive integer, which is in the range of [1, 10000]. All the integers in the array will be in the range of [-10000, 10000].
思路:转化为数组先排序,然后取第1个数、第3个数、第5个数......的和。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int arrayPairSum(int[] nums) { 3 if (nums == null || nums.length == 0 || nums.length % 2 == 1) { 4 return 0; 5 } 6 int result = 0; 7 Arrays.sort(nums); 8 for (int i = 0; i < nums.length; i = i + 2) { 9 result += nums[i]; 10 } 11 return result; 12 } 13 }
485. Max Consecutive Ones
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a binary array, find the maximum number of consecutive 1s in this array. Example 1: Input: [1,1,0,1,1,1] Output: 3 Explanation: The first two digits or the last three digits are consecutive 1s. The maximum number of consecutive 1s is 3. Note: The input array will only contain 0 and 1. The length of input array is a positive integer and will not exceed 10,000
思路:遍历一遍,统计。简单
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int findMaxConsecutiveOnes(int[] nums) { 3 if (nums == null || nums.length == 0) { 4 return 0; 5 } 6 int result = 0; 7 int count = 0; 8 for (int i = 0; i < nums.length; i++) { 9 if (nums[i] == 0) { 10 count = 0; 11 continue; 12 } else { 13 while (i < nums.length && nums[i] == 1) { 14 count++; 15 i++; 16 } 17 result = Math.max(result, count); 18 count = 0; 19 } 20 21 } 22 return result; 23 } 24 }
560. Subarray Sum Equals K
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of integers and an integer k, you need to find the total number of continuous subarrays whose sum equals to k. Example 1: Input:nums = [1,1,1], k = 2 Output: 2 Note: The length of the array is in range [1, 20,000]. The range of numbers in the array is [-1000, 1000] and the range of the integer k is [-1e7, 1e7].
思路I:暴力法,前缀数组和与处理,然后计算所有子数组的和,统计子数组和等于k的个数,时间复杂度O(n^2)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int subarraySum(int[] nums, int k) { 3 if (nums == null || nums.length == 0) { 4 return 0; 5 } 6 int n = nums.length; 7 int[] sum = new int[n + 1]; 8 for (int i = 1; i <= n; i++) { 9 sum[i] = sum[i - 1] + nums[i - 1]; 10 } 11 int count = 0; 12 for (int i = 1; i <= n; i++) { 13 for (int j = 0; j < i; j++) { 14 if (sum[i] - sum[j] == k) { 15 count++; 16 } 17 } 18 } 19 return count; 20 } 21 }
思路II:用HashMap来存储:子数组的和 --> 等于该和的子数组的个数。时间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int subarraySum(int[] nums, int k) { 3 if (nums == null || nums.length == 0) { 4 return 0; 5 } 6 HashMap<Integer, Integer> map = new HashMap<>(); 7 map.put(0, 1); 8 int count = 0; 9 int sum = 0; 10 for (int i = 0; i < nums.length; i++) { 11 sum += nums[i]; 12 if (map.containsKey(sum - k)) { 13 count += map.get(sum - k); 14 } 15 if (!map.containsKey(sum)) { 16 map.put(sum, 1); 17 } else { 18 map.put(sum, map.get(sum) + 1); 19 } 20 } 21 return count; 22 } 23 }
495. Teemo Attacking
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
In LLP world, there is a hero called Teemo and his attacking can make his enemy Ashe be in poisoned condition. Now, given the Teemo's attacking ascending time series towards Ashe and the poisoning time duration per Teemo's attacking, you need to output the total time that Ashe is in poisoned condition. You may assume that Teemo attacks at the very beginning of a specific time point, and makes Ashe be in poisoned condition immediately. Example 1: Input: [1,4], 2 Output: 4 Explanation: At time point 1, Teemo starts attacking Ashe and makes Ashe be poisoned immediately. This poisoned status will last 2 seconds until the end of time point 2. And at time point 4, Teemo attacks Ashe again, and causes Ashe to be in poisoned status for another 2 seconds. So you finally need to output 4. Example 2: Input: [1,2], 2 Output: 3 Explanation: At time point 1, Teemo starts attacking Ashe and makes Ashe be poisoned. This poisoned status will last 2 seconds until the end of time point 2. However, at the beginning of time point 2, Teemo attacks Ashe again who is already in poisoned status. Since the poisoned status won't add up together, though the second poisoning attack will still work at time point 2, it will stop at the end of time point 3. So you finally need to output 3. Note: You may assume the length of given time series array won't exceed 10000. You may assume the numbers in the Teemo's attacking time series and his poisoning time duration per attacking are non-negative integers, which won't exceed 10,000,000.
思路:判断当前的开始点与之前延伸到的最大点是否有重叠,无重叠直接加上持续时间,有重叠就只加上除去重叠之外的持续时间。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int findPoisonedDuration(int[] timeSeries, int duration) { 3 if (timeSeries == null || timeSeries.length == 0 || duration <= 0) { 4 return 0; 5 } 6 int max = 0; 7 int total = 0; 8 for (int i = 0; i < timeSeries.length; i++) { 9 if (timeSeries[i] + duration <= max) { 10 continue; 11 } 12 if (timeSeries[i] >= max) { 13 total += duration; 14 } else { 15 total += timeSeries[i] + duration - max; 16 } 17 max = timeSeries[i] + duration; 18 } 19 return total; 20 } 21 }
380. Insert Delete GetRandom O(1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Design a data structure that supports all following operations in average O(1) time. insert(val): Inserts an item val to the set if not already present. remove(val): Removes an item val from the set if present. getRandom: Returns a random element from current set of elements. Each element must have the same probability of being returned. Example: // Init an empty set. RandomizedSet randomSet = new RandomizedSet(); // Inserts 1 to the set. Returns true as 1 was inserted successfully. randomSet.insert(1); // Returns false as 2 does not exist in the set. randomSet.remove(2); // Inserts 2 to the set, returns true. Set now contains [1,2]. randomSet.insert(2); // getRandom should return either 1 or 2 randomly. randomSet.getRandom(); // Removes 1 from the set, returns true. Set now contains [2]. randomSet.remove(1); // 2 was already in the set, so return false. randomSet.insert(2); // Since 2 is the only number in the set, getRandom always return 2.
思路:List存数据,Map存数据的索引,以方便remove操作O(1)时间内找到要删除元素的索引。等可能的返回List中的任一元素用Random函数在size范围内(0 ~ size - 1)等可能的取索引即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class RandomizedSet { 2 List<Integer> list; 3 Map<Integer, Integer> map; 4 Random random; 5 /** Initialize your data structure here. */ 6 public RandomizedSet() { 7 list = new ArrayList<>(); 8 map = new HashMap<>(); 9 random = new Random(); 10 } 11 12 /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */ 13 public boolean insert(int val) { 14 if (map.containsKey(val)) { 15 return false; 16 } 17 list.add(val); 18 map.put(val, list.size() - 1); 19 return true; 20 } 21 22 /** Removes a value from the set. Returns true if the set contained the specified element. */ 23 public boolean remove(int val) { 24 if (!map.containsKey(val)) { 25 return false; 26 } 27 int index = map.get(val); 28 if (index < list.size() - 1) { 29 list.set(index, list.get(list.size() - 1)); 30 // 这段代码可有可无,反正是要删去的,可以不用修改。 31 //list.set(list.size() - 1, val); 32 map.put(list.get(index), index); 33 // 这段代码可有可无,反正是要删去的,可以不用修改。 34 //map.put(list.get(list.size() - 1), list.size() - 1); 35 } 36 list.remove(list.size() - 1); 37 map.remove(val); 38 return true; 39 } 40 41 /** Get a random element from the set. */ 42 public int getRandom() { 43 return list.get(random.nextInt(list.size())); 44 } 45 } 46 47 /** 48 * Your RandomizedSet object will be instantiated and called as such: 49 * RandomizedSet obj = new RandomizedSet(); 50 * boolean param_1 = obj.insert(val); 51 * boolean param_2 = obj.remove(val); 52 * int param_3 = obj.getRandom(); 53 */
493. Reverse Pairs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array nums, we call (i, j) an important reverse pair if i < j and nums[i] > 2*nums[j]. You need to return the number of important reverse pairs in the given array. Example1: Input: [1,3,2,3,1] Output: 2 Example2: Input: [2,4,3,5,1] Output: 3 Note: The length of the given array will not exceed 50,000. All the numbers in the input array are in the range of 32-bit integer.
思路:这道题就是在merge sort的过程中,加入一个计数的过程。对于分割的两部分,都是排好序的,对两个部分分别计两个索引i和j,固定i,在第二部分里找出满足nums[i] > 2 * nums[j]的个数,然后循环i就可以得到两部分之间有多少pairs。那么总的pairs就是前一部分的pairs(下一次的mergeSort要统计的)+后一部分的pairs(下一次的mergeSort要统计的)+两个部分之间的pairs。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int reversePairs(int[] nums) { 3 if (nums == null || nums.length <= 1) { 4 return 0; 5 } 6 int[] temp = new int[nums.length]; 7 return mergeSort(nums, 0, nums.length - 1, temp); 8 } 9 // mergeSort模版程序 10 public int mergeSort(int[] nums, int start, int end, int[] temp) { 11 if (start >= end) { 12 return 0; 13 } 14 int mid = start + (end - start) / 2; 15 int count = mergeSort(nums, start, mid, temp) + mergeSort(nums, mid + 1, end, temp); 16 // 注意这里j = mid + 1要写在for里面,因为前一个i满足nums[i] / 2.0 > nums[j]的话, 17 // 由于两部分都是排好序的,下一个i一定也满足nums[i] / 2.0 > nums[j],所以j不用再回退到 18 // mid + 1这个起始位置,直接从当前位置开始判断。否则会超时,一定要注意。 19 for (int i = start, j = mid + 1; i <= mid; i++) { 20 // 这里nums[i] / 2.0 > nums[j]而不是写成nums[i] > 2 * nums[j]是为了避免2 * nums[j]溢出 21 while (j <= end && nums[i] / 2.0 > nums[j]) { 22 j++; 23 } 24 count += j - (mid + 1); 25 } 26 merge(nums, start, end, temp); 27 return count; 28 } 29 public void merge(int[] nums, int start, int end, int[] temp) { 30 int mid = start + (end - start) / 2; 31 int leftIndex = start; 32 int rightIndex = mid + 1; 33 int index = start; 34 while (leftIndex <= mid && rightIndex <= end) { 35 if (nums[leftIndex] < nums[rightIndex]) { 36 temp[index++] = nums[leftIndex++]; 37 } else { 38 temp[index++] = nums[rightIndex++]; 39 } 40 } 41 while (leftIndex <= mid) { 42 temp[index++] = nums[leftIndex++]; 43 } 44 while (rightIndex <= end) { 45 temp[index++] = nums[rightIndex++]; 46 } 47 for (int i = start; i <= end; i++) { 48 nums[i] = temp[i]; 49 } 50 } 51 }
315. Count of Smaller Numbers After Self
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i]. Example: Given nums = [5, 2, 6, 1] To the right of 5 there are 2 smaller elements (2 and 1). To the right of 2 there is only 1 smaller element (1). To the right of 6 there is 1 smaller element (1). To the right of 1 there is 0 smaller element. Return the array [2, 1, 1, 0].
思路:为了方便统计nums中的每个数在它后面有多少个数比它小,对nums的索引而不是nums的元素值来merge sort,每次对排好序的前后两部分进行merge的过程中,对于第一部分的索引i,统计在第二部分中有多少个数小于i对应的元素值,然后将统计的个数加进count[indexes[i]]中,indexes[i]为nums数组中元素的索引。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 // 定义为类变量,不用每次将其作为函数参数传递下去。 3 int[] count; 4 public List<Integer> countSmaller(int[] nums) { 5 List<Integer> result = new ArrayList<>(); 6 if (nums == null || nums.length == 0) { 7 return result; 8 } 9 count = new int[nums.length]; 10 int[] temp = new int[nums.length]; 11 int[] indexes = new int[nums.length]; 12 // 为了方便统计nums中的每个数在它后面有多少个数比它小,对nums的索引而不是nums的元素值来merge sort,所以注意索引的初始化!不能掉! 13 for (int i = 0; i < indexes.length; i++) { 14 indexes[i] = i; 15 } 16 mergeSort(nums, indexes, 0, nums.length - 1, temp); 17 for (int i = 0; i < count.length; i++) { 18 result.add(count[i]); 19 } 20 return result; 21 } 22 // 归并排序模版程序,背下来。 23 public void mergeSort(int[] nums, int[] indexes, int start, int end, int[] temp) { 24 if (start >= end) { 25 return; 26 } 27 int mid = start + (end - start) / 2; 28 mergeSort(nums, indexes, start, mid, temp); 29 mergeSort(nums, indexes, mid + 1, end, temp); 30 merge(nums, indexes, start, end, temp); 31 } 32 public void merge(int[] nums, int[] indexes, int start, int end, int[] temp) { 33 int mid = start + (end - start) / 2; 34 int leftIndex = start; 35 int rightIndex = mid + 1; 36 int sortIndex = start; 37 // pairs用来统计一次merge的过程中,对于第一部分的索引i,在第二部分中有多少个数小于i对应的元素值。 38 int pairs = 0; 39 while (leftIndex <= mid && rightIndex <= end) { 40 if (nums[indexes[leftIndex]] > nums[indexes[rightIndex]]) { 41 temp[sortIndex++] = indexes[rightIndex++]; 42 pairs++; 43 } else { 44 count[indexes[leftIndex]] += pairs; 45 temp[sortIndex++] = indexes[leftIndex++]; 46 } 47 } 48 while (leftIndex <= mid) { 49 count[indexes[leftIndex]] += pairs; 50 temp[sortIndex++] = indexes[leftIndex++]; 51 } 52 while (rightIndex <= end) { 53 temp[sortIndex++] = indexes[rightIndex++]; 54 } 55 for (int i = start; i <= end; i++) { 56 indexes[i] = temp[i]; 57 } 58 } 59 }
327. Count of Range Sum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive. Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i ≤ j), inclusive. Note: A naive algorithm of O(n2) is trivial. You MUST do better than that. Example: Given nums = [-2, 5, -1], lower = -2, upper = 2, Return 3. The three ranges are : [0, 0], [2, 2], [0, 2] and their respective sums are: -2, -1, 2.
思路:首先对给定的数组进行前缀和预处理,然后对前缀和数组进行merge sort,每次merge之前,对于前半部分固定的索引i,在后半部分中分别找出第一个满足prefixSum[k] - prefixSum[i] >= lower的索引k和prefixSum[j] - prefixSum[i] > upper的索引j,则j - k就是两部分之间满足条件的子区间个数,由于左右区间都是递增的,i向右移动时j和k也随之右移而不需要回溯到起始位置,所以在O(n)的复杂度内可以找出两部分之间的满足条件的子区间个数。总的满足条件的子区间个数 = 前一部分满足条件的子区间个数(下一次的mergeSort要统计的) + 后一部分满足条件的子区间个数(下一次的mergeSort要统计的) + 两个部分之间满足条件的子区间个数(j - k)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int countRangeSum(int[] nums, int lower, int upper) { 3 if (nums == null || nums.length == 0) { 4 return 0; 5 } 6 // 为了避免前缀和溢出,定义为long型数组。 7 long[] prefixSum = new long[nums.length + 1]; 8 // 前缀和预处理,可以当成一个模版记下来,prefixSum[i]表示nums数组中前i个元素的和。 9 for (int i = 1; i <= nums.length; i++) { 10 prefixSum[i] = prefixSum[i - 1] + nums[i - 1]; 11 } 12 long[] temp = new long[nums.length + 1]; 13 return mergeSort(prefixSum, 0, nums.length, lower, upper, temp); 14 } 15 public int mergeSort(long[] prefixSum, int start, int end, int lower, int upper, long[] temp) { 16 if (start >= end) { 17 return 0; 18 } 19 int mid = start + (end - start) / 2; 20 int count = mergeSort(prefixSum, start, mid, lower, upper, temp) 21 + mergeSort(prefixSum, mid + 1, end, lower, upper, temp); 22 int j = mid + 1; 23 int k = mid + 1; 24 for (int i = start; i <= mid; i++) { 25 while (k <= end && prefixSum[k] - prefixSum[i] < lower) { 26 k++; 27 } 28 while (j <= end && prefixSum[j] - prefixSum[i] <= upper) { 29 j++; 30 } 31 count += j - k; 32 } 33 merge(prefixSum, start, end, temp); 34 return count; 35 } 36 public void merge(long[] prefixSum, int start, int end, long[] temp) { 37 int mid = start + (end - start) / 2; 38 int leftIndex = start; 39 int rightIndex = mid + 1; 40 int index = start; 41 while (leftIndex <= mid && rightIndex <= end) { 42 if (prefixSum[leftIndex] < prefixSum[rightIndex]) { 43 temp[index++] = prefixSum[leftIndex++]; 44 } else { 45 temp[index++] = prefixSum[rightIndex++]; 46 } 47 } 48 while (leftIndex <= mid) { 49 temp[index++] = prefixSum[leftIndex++]; 50 } 51 while (rightIndex <= end) { 52 temp[index++] = prefixSum[rightIndex++]; 53 } 54 for (int i = start; i <= end; i++) { 55 prefixSum[i] = temp[i]; 56 } 57 } 58 }
494. Target Sum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol. Find out how many ways to assign symbols to make sum of integers equal to target S. Example 1: Input: nums is [1, 1, 1, 1, 1], S is 3. Output: 5 Explanation: -1+1+1+1+1 = 3 +1-1+1+1+1 = 3 +1+1-1+1+1 = 3 +1+1+1-1+1 = 3 +1+1+1+1-1 = 3 There are 5 ways to assign symbols to make the sum of nums be target 3. Note: The length of the given array is positive and will not exceed 20. The sum of elements in the given array will not exceed 1000. Your output answer is guaranteed to be fitted in a 32-bit integer.
思路:DFS + memorization 对于所有可能的情况用递归来实现,为了避免重复递归,用一个Map存储中间结果。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int findTargetSumWays(int[] nums, int S) { 3 if (nums == null || nums.length == 0) { 4 return 0; 5 } 6 Map<String, Integer> map = new HashMap<>(); 7 return dfsHelper(nums, 0, 0, S, map); 8 } 9 public int dfsHelper(int[] nums, int index, int sum, int S, Map<String, Integer> map) { 10 String key = index + " " + sum; 11 if (map.containsKey(key)) { 12 return map.get(key); 13 } 14 if (index == nums.length) { 15 if (sum == S) { 16 return 1; 17 } else { 18 return 0; 19 } 20 } 21 int add = dfsHelper(nums, index + 1, sum + nums[index], S, map); 22 int minus = dfsHelper(nums, index + 1, sum - nums[index], S, map); 23 map.put(key, add + minus); 24 return add + minus; 25 } 26 }
491. Increasing Subsequences
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an integer array, your task is to find all the different possible increasing subsequences of the given array, and the length of an increasing subsequence should be at least 2 . Example: Input: [4, 6, 7, 7] Output: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]] Note: The length of the given array will not exceed 15. The range of integer in the given array is [-100,100]. The given array may contain duplicates, and two equal integers should also be considered as a special case of increasing sequence.
思路:DFS遍历所有情况,用一个tempList存储每次递归中的递增序列,如果遇到递减序列,即当前元素小于tempList中的最后一个元素,就结束当前递归。每次递归只要tempList的大小大于等于2就加入到set中,选set而不是list是为了在O(1)时间内去重,而用list的contains方法不能在O(1)时间内去重。最后将set中的元素加入到结果中。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<List<Integer>> findSubsequences(int[] nums) { 3 List<List<Integer>> results = new ArrayList<>(); 4 Set<List<Integer>> set = new HashSet<>(); 5 if (nums == null || nums.length <= 1) { 6 return results; 7 } 8 dfsHelper(set, nums, 0, new ArrayList<Integer>()); 9 return new ArrayList<List<Integer>>(set); 10 } 11 public void dfsHelper(Set<List<Integer>> set, int[] nums, int startIndex, List<Integer> tempList) { 12 if (tempList.size() >= 2) { 13 set.add(new ArrayList<Integer>(tempList)); 14 } 15 for (int i = startIndex; i < nums.length; i++) { 16 if (tempList.size() == 0 || tempList.get(tempList.size() - 1) <= nums[i]) { 17 tempList.add(nums[i]); 18 dfsHelper(set, nums, i + 1, tempList); 19 tempList.remove(tempList.size() - 1); 20 } 21 } 22 } 23 }
417. Pacific Atlantic Water Flow
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an m x n matrix of non-negative integers representing the height of each unit cell in a continent, the "Pacific ocean" touches the left and top edges of the matrix and the "Atlantic ocean" touches the right and bottom edges. Water can only flow in four directions (up, down, left, or right) from a cell to another one with height equal or lower. Find the list of grid coordinates where water can flow to both the Pacific and Atlantic ocean. Note: The order of returned grid coordinates does not matter. Both m and n are less than 150. Example: Given the following 5x5 matrix: Pacific ~ ~ ~ ~ ~ ~ 1 2 2 3 (5) * ~ 3 2 3 (4) (4) * ~ 2 4 (5) 3 1 * ~ (6) (7) 1 4 5 * ~ (5) 1 1 2 4 * * * * * * Atlantic Return: [[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (positions with parentheses in above matrix).
思路:为了让水能够流进海洋里,从海洋边界出发,逆向找高度大于等于当前所在节点的高度,这是一个bfs的过程,这个过程中所访问到的节点就是能够到达海洋的节点。然后取所有那些水流能同时到达两个海洋的节点加入到结果中即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 public Node(int x, int y) { 5 this.x = x; 6 this.y = y; 7 } 8 } 9 public class Solution { 10 int n; 11 int m; 12 public List<int[]> pacificAtlantic(int[][] matrix) { 13 List<int[]> result = new ArrayList<>(); 14 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 15 return result; 16 } 17 n = matrix.length; 18 m = matrix[0].length; 19 boolean[][] pacific = new boolean[n][m]; 20 boolean[][] atlantic = new boolean[n][m]; 21 for (int i = 0; i < n; i++) { 22 if (!pacific[i][0]) { 23 bfs(matrix, i, 0, pacific); 24 } 25 } 26 for (int j = 1; j < m; j++) { 27 if (!pacific[0][j]) { 28 bfs(matrix, 0, j, pacific); 29 } 30 } 31 for (int i = 0; i < n; i++) { 32 if (!atlantic[i][m - 1]) { 33 bfs(matrix, i, m - 1, atlantic); 34 } 35 } 36 for (int j = 0; j < m - 1; j++) { 37 if (!atlantic[n - 1][j]) { 38 bfs(matrix, n - 1, j, atlantic); 39 } 40 } 41 for (int i = 0; i < n; i++) { 42 for (int j = 0; j < m; j++) { 43 if (pacific[i][j] && atlantic[i][j]) { 44 result.add(new int[]{i, j}); 45 } 46 } 47 } 48 return result; 49 } 50 public void bfs(int[][] matrix, int x, int y, boolean[][] visited) { 51 int[] dx = {1, 0, -1, 0}; 52 int[] dy = {0, 1, 0, -1}; 53 Node node = new Node(x, y); 54 Queue<Node> queue = new LinkedList<>(); 55 queue.offer(node); 56 visited[node.x][node.y] = true; 57 while (!queue.isEmpty()) { 58 Node cur = queue.poll(); 59 for (int j = 0; j < 4; j++) { 60 int nextX = cur.x + dx[j]; 61 int nextY = cur.y + dy[j]; 62 if (nextX >= 0 && nextX < n && nextY >= 0 && nextY < m 63 && !visited[nextX][nextY] && matrix[nextX][nextY] >= matrix[cur.x][cur.y]) { 64 Node next = new Node(nextX, nextY); 65 queue.offer(next); 66 visited[next.x][next.y] = true; 67 } 68 } 69 } 70 } 71 }
542. 01 Matrix
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a matrix consists of 0 and 1, find the distance of the nearest 0 for each cell. The distance between two adjacent cells is 1. Example 1: Input: 0 0 0 0 1 0 0 0 0 Output: 0 0 0 0 1 0 0 0 0 Example 2: Input: 0 0 0 0 1 0 1 1 1 Output: 0 0 0 0 1 0 1 2 1 Note: The number of elements of the given matrix will not exceed 10,000. There are at least one 0 in the given matrix. The cells are adjacent in only four directions: up, down, left and right.
思路:这道题一开始的想法是对矩阵中的每一个1进行分层BFS找到离这个1距离最近的0,这样的话会出现重复BFS的问题,容易超时。可以从0开始出发,对于01二维数组,起始的时候做预处理:对于矩阵中的1置为Integer.MAX_VALUE,对于矩阵中的0就将当前位置节点加入到队列中。BFS过程中,新计算出的距离(1 + matrix[i][j])小于当前距离的话才更新节点距离,并且入列,否则跳过。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 public Node(int x, int y) { 5 this.x = x; 6 this.y = y; 7 } 8 } 9 public class Solution { 10 public int[][] updateMatrix(int[][] matrix) { 11 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 12 return new int[0][0]; 13 } 14 int n = matrix.length; 15 int m = matrix[0].length; 16 Queue<Node> queue = new LinkedList<>(); 17 for (int i = 0; i < n; i++) { 18 for (int j = 0; j < m; j++) { 19 if (matrix[i][j] == 1) { 20 matrix[i][j] = Integer.MAX_VALUE; 21 } else { 22 queue.offer(new Node(i, j)); 23 } 24 } 25 } 26 int[] dx ={1, 0, -1, 0}; 27 int[] dy = {0, 1, 0, -1}; 28 while (!queue.isEmpty()) { 29 Node cur = queue.poll(); 30 for (int j = 0; j < 4; j++) { 31 int nextX = cur.x + dx[j]; 32 int nextY = cur.y + dy[j]; 33 if (nextX >= 0 && nextX < n && nextY >= 0 && nextY < m && 1 + matrix[cur.x][cur.y] < matrix[nextX][nextY]) { 34 matrix[nextX][nextY] = 1 + matrix[cur.x][cur.y]; 35 Node next = new Node(nextX, nextY); 36 queue.offer(next); 37 } 38 } 39 } 40 return matrix; 41 } 42 }
sort系列:
56. Merge Intervals
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a collection of intervals, merge all overlapping intervals. For example, Given [1,3],[2,6],[8,10],[15,18], return [1,6],[8,10],[15,18]. Subscribe to see which companies asked this question.
思路:先对list中的intervlas按照interval的start大小进行排序,注意没有必要再接着对interval的end排序,end是用来更新的。然后对于每一个interval,如果当前interval的start小于前一个interval的end,说明当前interval与前一个interval有重叠,那么更新前一个interval的end;否则说明没有重叠,直接将当前interval加入到结果集中。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for an interval. 3 * public class Interval { 4 * int start; 5 * int end; 6 * Interval() { start = 0; end = 0; } 7 * Interval(int s, int e) { start = s; end = e; } 8 * } 9 */ 10 public class Solution { 11 public List<Interval> merge(List<Interval> intervals) { 12 List<Interval> result = new ArrayList<>(); 13 if (intervals == null || intervals.size() == 0) { 14 return result; 15 } 16 Collections.sort(intervals, new Comparator<Interval>(){ 17 public int compare(Interval a, Interval b) { 18 return a.start - b.start; 19 } 20 }); 21 for (Interval interval : intervals) { 22 if (result.size() == 0 || interval.start > result.get(result.size() - 1).end) { 23 result.add(interval); 24 } else { 25 Interval preInterval = result.get(result.size() - 1); 26 //多余的代码 27 //preInterval.start = Math.min(preInterval.start, interval.start); 28 preInterval.end = Math.max(preInterval.end, interval.end); 29 } 30 } 31 return result; 32 } 33 }
179. Largest Number
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a list of non negative integers, arrange them such that they form the largest number. For example, given [3, 30, 34, 5, 9], the largest formed number is 9534330. Note: The result may be very large, so you need to return a string instead of an integer.
思路:自定义比较函数来排序,对于两个字符串a和b,s1=a+b,s2=b+a,拼接串的比较可以直接使用库函数s2.compareTo(s1)来处理,要善于利用库函数,不要重复造轮子!同时注意边界情况:数组全为0,如[0, 0, 0, 0],此时不应该返回"0000",而应该返回"0"。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public String largestNumber(int[] nums) { 3 if (nums == null || nums.length == 0) { 4 return ""; 5 } 6 String[] str = new String[nums.length]; 7 for (int i = 0; i < str.length; i++) { 8 str[i] = String.valueOf(nums[i]); 9 } 10 Arrays.sort(str, new Comparator<String>() { 11 public int compare(String a, String b) { 12 String s1 = a + b; 13 String s2 = b + a; 14 return s2.compareTo(s1); 15 } 16 }); 17 // 边界情况:数组元素全为0,比如[0, 0, 0, 0] 18 if (str[0].charAt(0) == '0') { 19 return "0"; 20 } 21 StringBuilder sb = new StringBuilder(); 22 for (String temp : str) { 23 sb.append(temp); 24 } 25 return sb.toString(); 26 } 27 }
57. Insert Interval
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary). You may assume that the intervals were initially sorted according to their start times. Example 1: Given intervals [1,3],[6,9], insert and merge [2,5] in as [1,5],[6,9]. Example 2: Given [1,2],[3,5],[6,7],[8,10],[12,16], insert and merge [4,9] in as [1,2],[3,10],[12,16]. This is because the new interval [4,9] overlaps with [3,5],[6,7],[8,10].
思路:三步走。第一步:把非重叠的在newInterval前面的interval加入到结果集中。第二步:用与newInterval重叠的interval来更新newInterval的start和end。第三步:把非重叠的在newInterval后面的interval加入到结果集中。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for an interval. 3 * public class Interval { 4 * int start; 5 * int end; 6 * Interval() { start = 0; end = 0; } 7 * Interval(int s, int e) { start = s; end = e; } 8 * } 9 */ 10 public class Solution { 11 public List<Interval> insert(List<Interval> intervals, Interval newInterval) { 12 List<Interval> result = new ArrayList<>(); 13 int i = 0; 14 while (i < intervals.size() && intervals.get(i).end < newInterval.start) { 15 result.add(intervals.get(i)); 16 i++; 17 } 18 while (i < intervals.size() && intervals.get(i).start <= newInterval.end) { 19 newInterval.start = Math.min(newInterval.start, intervals.get(i).start); 20 newInterval.end = Math.max(newInterval.end, intervals.get(i).end); 21 i++; 22 } 23 result.add(newInterval); 24 while (i < intervals.size()) { 25 result.add(intervals.get(i)); 26 i++; 27 } 28 return result; 29 } 30 }
567. Permutation in String
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two strings s1 and s2, write a function to return true if s2 contains the permutation of s1. In other words, one of the first string's permutations is the substring of the second string. Example 1: Input:s1 = "ab" s2 = "eidbaooo" Output:True Explanation: s2 contains one permutation of s1 ("ba"). Example 2: Input:s1= "ab" s2 = "eidboaoo" Output: False Note: The input strings only contain lower case letters. The length of both given strings is in range [1, 10,000].
思路:
知识点1:如何判断s1是s2的全排列(两个字符串长度相等)?用map分别统计s1和s2的 字符 --> 该字符出现的次数。如果每个字符及其出现次数相等,那么s1就是s2的券排列。由于只有26个字符,也可以开一个长度为26的计数数组来统计判断。
知识点2:如何判断s2中含有s1的全排列,即如何判断s2是否含有子串是s1的全排列。由知识点1我们很自然地想到,用知识点1的办法解决知识点2。用一个与s1长度相等的滑动窗口对s2从开始移动到末尾,开始时遍历s1的每个字符,将字符的次数统计在map/计数数组中,滑动窗口每滑动到一个新地方,对滑入窗口的字符,在map/计数数组中将该字符对应的次数减1,对滑出窗口的字符,在map/计数数组中将该字符对应的次数加1,如果此时map的每个value/计数数组里的每个元素都为0,说明此时滑动窗口对应的子串是s1的全排列,返回true。如果窗口滑到末尾也没找到,返回false。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean checkInclusion(String s1, String s2) { 3 if (s1 == null) { 4 return true; 5 } 6 if (s1 != null && s2 == null) { 7 return false; 8 } 9 if (s1.length() > s2.length()) { 10 return false; 11 } 12 int[] count = new int[26]; 13 for (int i = 0; i < s1.length(); i++) { 14 count[s1.charAt(i) - 'a']++; 15 count[s2.charAt(i) - 'a']--; 16 } 17 if (allZero(count)) { 18 return true; 19 } 20 for (int i = s1.length(); i < s2.length(); i++) { 21 count[s2.charAt(i) - 'a']--; 22 count[s2.charAt(i - s1.length()) - 'a']++; 23 if (allZero(count)) { 24 return true; 25 } 26 } 27 return false; 28 } 29 public boolean allZero(int[] count) { 30 for (int i = 0; i < count.length; i++) { 31 if (count[i] != 0) { 32 return false; 33 } 34 } 35 return true; 36 } 37 }
11. Container With Most Water
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.
Note: You may not slant the container and n is at least 2.
思路:双指针,左右指针分别指向最外面的两条线,然后以这两条线开始计算所能装的容积,找两个线的高度较短的一方,将相应的指针向中间靠拢,只有这样才可能得到比原来更大的容积。当两个指针相遇即结束,时间复杂度O(n),空间复杂度O(1)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maxArea(int[] height) { 3 if (height == null || height.length <= 1) { 4 return 0; 5 } 6 int start = 0; 7 int end = height.length - 1; 8 int maxArea = 0; 9 while (start < end) { 10 maxArea = Math.max(maxArea, Math.min(height[start], height[end]) * (end - start)); 11 if (height[start] < height[end]) { 12 start++; 13 } else { 14 end--; 15 } 16 } 17 return maxArea; 18 } 19 }
搜索到满意滑动窗口类型(3道)
30. Substring with Concatenation of All Words
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters. For example, given: s: "barfoothefoobarman" words: ["foo", "bar"] You should return the indices: [0,9]. (order does not matter).
思路:固定起点i(外层循环),从起点开始搜索(内层循环),如果能搜索到一个窗口恰好满足该窗口内的子串是words中所有单词的结合,就将起点i加入到结果集中;否则终止内层循环,i移到下一个起点。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<Integer> findSubstring(String s, String[] words) { 3 List<Integer> result = new ArrayList<>(); 4 if (s == null || s.length() == 0 || words == null || words.length == 0 5 || s.length() < words[0].length() * words.length) { 6 return result; 7 } 8 Map<String, Integer> map = new HashMap<>(); 9 for (int i = 0; i < words.length; i++) { 10 if (!map.containsKey(words[i])) { 11 map.put(words[i], 1); 12 } else { 13 map.put(words[i], map.get(words[i]) + 1); 14 } 15 } 16 int wordLen = words[0].length(); 17 for (int i = 0; i <= s.length() - words.length * wordLen; i++) { 18 Map<String, Integer> temp = new HashMap<>(); 19 int j = 0; 20 for (j = 0; j < words.length; j++) { 21 String word = s.substring(i + j * wordLen, i + (j + 1) * wordLen); 22 if (!map.containsKey(word)) { 23 break; 24 } 25 if (!temp.containsKey(word)) { 26 temp.put(word, 1); 27 } else { 28 temp.put(word, temp.get(word) + 1); 29 } 30 if (temp.get(word) > map.get(word)) { 31 break; 32 } 33 } 34 if (j == words.length) { 35 result.add(i); 36 } 37 } 38 return result; 39 } 40 }
76. Minimum Window Substring
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n). For example, S = "ADOBECODEBANC" T = "ABC" Minimum window is "BANC". Note: If there is no such window in S that covers all characters in T, return the empty string "". If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.
思路:固定起点i,从起点开始搜索,如果能搜索到一个窗口恰好满足该窗口内的子串含有t中所有字符,就用该窗口长度和窗口对应的子串更新最小长度和最小子串;否则移到下一个起点开始继续遍历。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public String minWindow(String s, String t) { 3 if (s == null || s.length() == 0 || t == null || t.length() == 0) { 4 return ""; 5 } 6 int[] targetHash = initialize(t); 7 int[] sourceHash = new int[256]; 8 int i = 0; 9 int j = 0; 10 int minLength = Integer.MAX_VALUE; 11 String minString = ""; 12 for (i = 0; i < s.length(); i++) { 13 while (j < s.length() && !valid(sourceHash, targetHash)) { 14 sourceHash[s.charAt(j)]++; 15 j++; 16 } 17 if (valid(sourceHash, targetHash)) { 18 if (j - i < minLength) { 19 minLength = j - i; 20 minString = s.substring(i, j); 21 } 22 } 23 sourceHash[s.charAt(i)]--; 24 } 25 return minString; 26 } 27 public int[] initialize(String t) { 28 int[] targetHash = new int[256]; 29 for (int i = 0; i < t.length(); i++) { 30 targetHash[t.charAt(i)]++; 31 } 32 return targetHash; 33 } 34 public boolean valid(int[] sourceHash, int[] targetHash) { 35 for (int i = 0; i < sourceHash.length; i++) { 36 if (sourceHash[i] < targetHash[i]) { 37 return false; 38 } 39 } 40 return true; 41 } 42 }
3. Longest Substring Without Repeating Characters
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a string, find the length of the longest substring without repeating characters. Examples: Given "abcabcbb", the answer is "abc", which the length is 3. Given "bbbbb", the answer is "b", with the length of 1. Given "pwwkew", the answer is "wke", with the length of 3. Note that the answer must be a substring, "pwke" is a subsequence and not a substring.
思路:固定起点i,从起点开始搜索,如果能搜索到一个窗口恰好满足该窗口内没有重复的字符(set实现)且窗口长度最大,就用该窗口长度更新结果,遇到窗口内出现重复字符时停止向前扩大窗口,移动到下一个起点继续上述过程。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int lengthOfLongestSubstring(String s) { 3 if (s == null || s.length() == 0) { 4 return 0; 5 } 6 int i = 0; 7 int j = 0; 8 int max = Integer.MIN_VALUE; 9 Set<Character> set = new HashSet<>(); 10 for (i = 0; i < s.length(); i++) { 11 while (j < s.length() && !set.contains(s.charAt(j))) { 12 set.add(s.charAt(j)); 13 j++; 14 } 15 max = Math.max(max, set.size()); 16 set.remove(s.charAt(i)); 17 } 18 return max; 19 } 20 }
------------ 我是分割线 -------------
从今天开始,6.5号,周一。每天跟着师兄按专题按AC率刷两道题,每晚交流今日刷的题目,扫荡式!要坚持下去,保持良好的习惯,fighting!做过的题,争取一遍AC,写完代码一定要检查一下corner case和有没有变量名、函数名敲错,这都是很容易犯的错误!!!
BFS专题:
513. Find Bottom Left Tree Value
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a binary tree, find the leftmost value in the last row of the tree. Example 1: Input: 2 / \ 1 3 Output: 1 Example 2: Input: 1 / \ 2 3 / / \ 4 5 6 / 7 Output: 7 Note: You may assume the tree (i.e., the given root node) is not NULL.
题意:找出二叉树里,最后一层最左边的节点的值。
思路I:BFS,分层,用每一层的第一个节点更新leftmost,BFS结束之后leftmost就是所要找的最后一层最左边的节点,返回节点值即可。
一次AC
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public int findBottomLeftValue(TreeNode root) { 12 if (root == null) { 13 return 0; 14 } 15 Queue<TreeNode> queue = new LinkedList<>(); 16 queue.offer(root); 17 TreeNode leftmost = null; 18 while (!queue.isEmpty()) { 19 int size = queue.size(); 20 for (int i = 0; i < size; i++) { 21 TreeNode node = queue.poll(); 22 if (i == 0) { 23 leftmost = node; 24 } 25 if (node.left != null) { 26 queue.offer(node.left); 27 } 28 if (node.right != null) { 29 queue.offer(node.right); 30 } 31 } 32 } 33 return leftmost.val; 34 } 35 }
思路II:DFS,设置一个全局深度global_depth和一个全局值global_value(以根节点为深度0的话,初始化为-1而不是0),这道题就是在前序遍历过程中,每当遍历到叶节点就检查当前深度是否大于全局深度,如果大于,更新全局深度和全局值,否则,继续遍历。相同深度只用存储第一个,由于前序遍历(其他遍历均可),也就是最左边的也节点。
515. Find Largest Value in Each Tree Row
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
You need to find the largest value in each row of a binary tree. Example: Input: 1 / \ 3 2 / \ \ 5 3 9 Output: [1, 3, 9]
题意:求二叉树每一层的最大值
思路I:BFS,分层,对于每层的节点统计出一个最大值加入到结果集中,最后返回结果集
一次AC
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public List<Integer> largestValues(TreeNode root) { 12 List<Integer> result = new ArrayList<>(); 13 if (root == null) { 14 return result; 15 } 16 Queue<TreeNode> queue = new LinkedList<>(); 17 queue.offer(root); 18 while (!queue.isEmpty()) { 19 int max = Integer.MIN_VALUE; 20 int size = queue.size(); 21 for (int i = 0; i < size; i++) { 22 TreeNode node = queue.poll(); 23 if (node.val > max) { 24 max = node.val; 25 } 26 if (node.left != null) { 27 queue.offer(node.left); 28 } 29 if (node.right != null) { 30 queue.offer(node.right); 31 } 32 } 33 result.add(max); 34 } 35 return result; 36 } 37 }
思路II:DFS,前序遍历过程中,利用当前递归深度去扩展list的size,list索引为二叉树深度(从0开始),值为当前深度的最大值,遍历过程中更新每个深度的最大值即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public List<Integer> largestValues(TreeNode root) { 12 List<Integer> result = new ArrayList<>(); 13 if (root == null) { 14 return result; 15 } 16 dfs(root, 0, result); 17 return result; 18 } 19 public void dfs(TreeNode root, int depth, List<Integer> result) { 20 if (root == null) { 21 return; 22 } 23 if (depth == result.size()) { 24 result.add(root.val); 25 } else { 26 result.set(depth, Math.max(result.get(depth), root.val)); 27 } 28 dfs(root.left, depth + 1, result); 29 dfs(root.right, depth + 1, result); 30 } 31 }
323. Number of Connected Components in an Undirected Graph
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to find the number of connected components in an undirected graph. Example 1: 0 3 | | 1 --- 2 4 Given n = 5 and edges = [[0, 1], [1, 2], [3, 4]], return 2. Example 2: 0 4 | | 1 --- 2 --- 3 Given n = 5 and edges = [[0, 1], [1, 2], [2, 3], [3, 4]], return 1. Note: You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.
题意:求连通快个数
思路I:BFS。最后看执行了多少次BFS操作。注意点已在代码中说明。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int countComponents(int n, int[][] edges) { 3 if (edges == null || edges.length == 0 || edges[0].length == 0) { 4 return n; 5 } 6 Map<Integer, Set<Integer>> map = initialize(n, edges); 7 boolean[] visited = new boolean[n]; 8 int count = 0; 9 for (int i = 0; i < n; i++) { 10 if (visited[i]) { 11 continue; 12 } 13 bfs(i, map, visited); 14 count++; 15 } 16 return count; 17 } 18 public void bfs(int start, Map<Integer, Set<Integer>> map, boolean[] visited) { 19 Queue<Integer> queue = new LinkedList<>(); 20 queue.offer(start); 21 visited[start] = true; 22 while (!queue.isEmpty()) { 23 int num = queue.poll(); 24 // 1.前提是每个节点的set域都非空才能遍历 25 for (int neighbor : map.get(num)) { 26 if (visited[neighbor]) { 27 continue; 28 } 29 queue.offer(neighbor); 30 visited[neighbor] = true; 31 } 32 } 33 } 34 public Map<Integer, Set<Integer>> initialize(int n, int[][] edges) { 35 Map<Integer, Set<Integer>> map = new HashMap<>(); 36 // 注意这里一定要把每个节点的set都要初始化,否则1处会报错 37 for (int i = 0; i < n; i++) { 38 map.put(i, new HashSet<Integer>()); 39 } 40 for (int i = 0; i < edges.length; i++) { 41 map.get(edges[i][0]).add(edges[i][1]); 42 map.get(edges[i][1]).add(edges[i][0]); 43 } 44 return map; 45 } 46 }
思路II:UnionFind。并查集结构中增设count用来统计连通块个数,初始化为节点个数n。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class UnionFind { 2 private int count; 3 private int[] father; 4 public UnionFind(int n) { 5 father = new int[n]; 6 for (int i = 0; i < n; i++) { 7 father[i] = i; 8 } 9 count = n; 10 } 11 public void union(int a, int b) { 12 int root_a = find(a); 13 int root_b = find(b); 14 if (root_a != root_b) { 15 father[root_a] = root_b; 16 count--; 17 } 18 } 19 public int find(int x) { 20 if (father[x] == x) { 21 return x; 22 } 23 return father[x] = find(father[x]); 24 } 25 public int getCount() { 26 return count; 27 } 28 } 29 public class Solution { 30 public int countComponents(int n, int[][] edges) { 31 if (n <= 0) { 32 return 0; 33 } 34 if (edges == null || edges.length == 0 || edges[0].length == 0) { 35 return n; 36 } 37 UnionFind union_find = new UnionFind(n); 38 for (int i = 0; i < edges.length; i++) { 39 union_find.union(edges[i][0], edges[i][1]); 40 } 41 return union_find.getCount(); 42 } 43 }
529. Minesweeper
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Let's play the minesweeper game (Wikipedia, online game)! You are given a 2D char matrix representing the game board. 'M' represents an unrevealed mine, 'E' represents an unrevealed empty square, 'B' represents a revealed blank square that has no adjacent (above, below, left, right, and all 4 diagonals) mines, digit ('1' to '8') represents how many mines are adjacent to this revealed square, and finally 'X' represents a revealed mine. Now given the next click position (row and column indices) among all the unrevealed squares ('M' or 'E'), return the board after revealing this position according to the following rules: If a mine ('M') is revealed, then the game is over - change it to 'X'. If an empty square ('E') with no adjacent mines is revealed, then change it to revealed blank ('B') and all of its adjacent unrevealed squares should be revealed recursively. If an empty square ('E') with at least one adjacent mine is revealed, then change it to a digit ('1' to '8') representing the number of adjacent mines. Return the board when no more squares will be revealed. Example 1: Input: [['E', 'E', 'E', 'E', 'E'], ['E', 'E', 'M', 'E', 'E'], ['E', 'E', 'E', 'E', 'E'], ['E', 'E', 'E', 'E', 'E']] Click : [3,0] Output: [['B', '1', 'E', '1', 'B'], ['B', '1', 'M', '1', 'B'], ['B', '1', '1', '1', 'B'], ['B', 'B', 'B', 'B', 'B']] Explanation: Example 2: Input: [['B', '1', 'E', '1', 'B'], ['B', '1', 'M', '1', 'B'], ['B', '1', '1', '1', 'B'], ['B', 'B', 'B', 'B', 'B']] Click : [1,2] Output: [['B', '1', 'E', '1', 'B'], ['B', '1', 'X', '1', 'B'], ['B', '1', '1', '1', 'B'], ['B', 'B', 'B', 'B', 'B']] Explanation: Note: The range of the input matrix's height and width is [1,50]. The click position will only be an unrevealed square ('M' or 'E'), which also means the input board contains at least one clickable square. The input board won't be a stage when game is over (some mines have been revealed). For simplicity, not mentioned rules should be ignored in this problem. For example, you don't need to reveal all the unrevealed mines when the game is over, consider any cases that you will win the game or flag any squares.
题意:扫雷游戏
思路:BFS,按照游戏规则来写思路即可,注意为了避免重复位置加入到队列中,可以有两种解决办法:1. 设置访问数组visited来记录哪些位置上的字符'E'被访问过 2. 每当加入一个'E'的位置到队列中时,修改字符'E'为字符'B'。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 public Node(int x, int y) { 5 this.x = x; 6 this.y = y; 7 } 8 } 9 public class Solution { 10 int n; 11 int m; 12 public char[][] updateBoard(char[][] board, int[] click) { 13 if (board == null || board.length == 0 || board[0].length == 0 || click == null || click.length == 0) { 14 return board; 15 } 16 n = board.length; 17 m = board[0].length; 18 int x = click[0]; 19 int y = click[1]; 20 if (board[x][y] != 'M' && board[x][y] != 'E') { 21 return board; 22 } 23 if (board[x][y] == 'M') { 24 board[x][y] = 'X'; 25 return board; 26 } 27 Queue<Node> queue = new LinkedList<>(); 28 queue.offer(new Node(x, y)); 29 // 方法1: 用一个访问数组记录那些位置的字符E被访问过了 30 // boolean[][] visited = new boolean[n][m]; 31 // visited[x][y] = true; 32 int[] dx = {1, 1, 0, -1, -1, -1, 0, 1}; 33 int[] dy = {0, 1, 1, 1, 0, -1, -1, -1}; 34 while (!queue.isEmpty()) { 35 Node cur = queue.poll(); 36 int count = 0; 37 List<Node> list = new ArrayList<>(); 38 for (int i = 0; i < 8; i++) { 39 Node next = new Node(cur.x + dx[i], cur.y + dy[i]); 40 if (next.x < 0 || next.x >= n || next.y < 0 || next.y >= m) { 41 continue; 42 } 43 if (board[next.x][next.y] == 'M') { 44 count++; 45 } 46 // if (!visited[next.x][next.y] && board[next.x][next.y] == 'E') { 47 // list.add(next); 48 // } 49 if (board[next.x][next.y] == 'E') { 50 list.add(next); 51 } 52 } 53 if (count == 0) { 54 board[cur.x][cur.y] = 'B'; 55 for (Node node : list) { 56 queue.offer(node); 57 // 方法2: 为了防止重复加入,当加入一个E时修改其字符为'B' 58 board[node.x][node.y] = 'B'; 59 // visited[node.x][node.y] = true; 60 } 61 } else { 62 board[cur.x][cur.y] = (char) ('0' + count); 63 } 64 } 65 return board; 66 } 67 }
286. Walls and Gates
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
You are given a m x n 2D grid initialized with these three possible values. -1 - A wall or an obstacle. 0 - A gate. INF - Infinity means an empty room. We use the value 231 - 1 = 2147483647 to represent INF as you may assume that the distance to a gate is less than 2147483647. Fill each empty room with the distance to its nearest gate. If it is impossible to reach a gate, it should be filled with INF. For example, given the 2D grid: INF -1 0 INF INF INF INF -1 INF -1 INF -1 0 -1 INF INF After running your function, the 2D grid should be: 3 -1 0 1 2 2 1 -1 1 -1 2 -1 0 -1 3 4
思路:BFS,分层,因为要统计最短路径,并且应该对二维矩阵中所有的0开始进行BFS遍历。时间复杂度O(knm),k为0的个数。
一刷的时候没AC是因为没有设置访问数组visited来避免元素重复入列,导致超时。
二刷AC。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 public Node(int x, int y) { 5 this.x = x; 6 this.y = y; 7 } 8 } 9 public class Solution { 10 int n; 11 int m; 12 public void wallsAndGates(int[][] rooms) { 13 if (rooms == null || rooms.length == 0 || rooms[0].length == 0) { 14 return; 15 } 16 n = rooms.length; 17 m = rooms[0].length; 18 for (int i = 0; i < n; i++) { 19 for (int j = 0; j < m; j++) { 20 if (rooms[i][j] == 0) { 21 bfs(rooms, i, j, new boolean[n][m]); 22 } 23 } 24 } 25 } 26 public void bfs(int[][] rooms, int x, int y, boolean[][] visited) { 27 int[] dx = {1, 0, -1, 0}; 28 int[] dy = {0, 1, 0, -1}; 29 Queue<Node> queue = new LinkedList<>(); 30 queue.offer(new Node(x, y)); 31 visited[x][y] = true; 32 int steps = 0; 33 while (!queue.isEmpty()) { 34 steps++; 35 int size = queue.size(); 36 for (int i = 0; i < size; i++) { 37 Node cur = queue.poll(); 38 for (int j = 0; j < 4; j++) { 39 Node next = new Node(cur.x + dx[j], cur.y + dy[j]); 40 if (inBound(next) && !visited[next.x][next.y] 41 && rooms[next.x][next.y] != 0 && rooms[next.x][next.y] != -1) { 42 queue.offer(next); 43 visited[next.x][next.y] = true; 44 rooms[next.x][next.y] = Math.min(rooms[next.x][next.y], steps); 45 } 46 } 47 } 48 } 49 } 50 public boolean inBound(Node node) { 51 if (node.x < 0 || node.x >= n) { 52 return false; 53 } 54 if (node.y < 0 || node.y >= m) { 55 return false; 56 } 57 return true; 58 } 59 }
优化:事先将所有为0的节点入列,以这些节点为中心,作为第一层开始进行BFS遍历,并且不用visited访问数组,因为只会将INTEGER.MAX_VALUE入列,每个更新过的节点都是最短路径。时间复杂度O(nm),空间复杂度O(1)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 public Node(int x, int y) { 5 this.x = x; 6 this.y = y; 7 } 8 } 9 public class Solution { 10 int n; 11 int m; 12 public void wallsAndGates(int[][] rooms) { 13 if (rooms == null || rooms.length == 0 || rooms[0].length == 0) { 14 return; 15 } 16 n = rooms.length; 17 m = rooms[0].length; 18 Queue<Node> queue = new LinkedList<>(); 19 for (int i = 0; i < n; i++) { 20 for (int j = 0; j < m; j++) { 21 if (rooms[i][j] == 0) { 22 queue.offer(new Node(i, j)); 23 } 24 } 25 } 26 int[] dx = {1, 0, -1, 0}; 27 int[] dy = {0, 1, 0, -1}; 28 int steps = 0; 29 while (!queue.isEmpty()) { 30 steps++; 31 int size = queue.size(); 32 for (int i = 0; i < size; i++) { 33 Node cur = queue.poll(); 34 for (int j = 0; j < 4; j++) { 35 Node next = new Node(cur.x + dx[j], cur.y + dy[j]); 36 if (inBound(next) && rooms[next.x][next.y] == Integer.MAX_VALUE) { 37 queue.offer(next); 38 rooms[next.x][next.y] = steps; 39 } 40 } 41 } 42 } 43 } 44 public boolean inBound(Node node) { 45 if (node.x < 0 || node.x >= n) { 46 return false; 47 } 48 if (node.y < 0 || node.y >= m) { 49 return false; 50 } 51 return true; 52 } 53 }
490. The Maze
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling up, down, left or right, but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction. Given the ball's start position, the destination and the maze, determine whether the ball could stop at the destination. The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the maze are all walls. The start and destination coordinates are represented by row and column indexes. Example 1 Input 1: a maze represented by a 2D array 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 Input 2: start coordinate (rowStart, colStart) = (0, 4) Input 3: destination coordinate (rowDest, colDest) = (4, 4) Output: true Explanation: One possible way is : left -> down -> left -> down -> right -> down -> right. Example 2 Input 1: a maze represented by a 2D array 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 Input 2: start coordinate (rowStart, colStart) = (0, 4) Input 3: destination coordinate (rowDest, colDest) = (3, 2) Output: false Explanation: There is no way for the ball to stop at the destination. Note: There is only one ball and one destination in the maze. Both the ball and the destination exist on an empty space, and they will not be at the same position initially. The given maze does not contain border (like the red rectangle in the example pictures), but you could assume the border of the maze are all walls. The maze contains at least 2 empty spaces, and both the width and height of the maze won't exceed 100.
思路:BFS,只不过这道题中BFS的步长不再为1,而是由往某一个方向一直走知道撞墙才能确定步长也就是确定下一次BFS的点。同样的我们用访问数组visited来避免重复元素入列。时间复杂度O(nm),空间复杂度O(nm),n为行数,m为列数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 public Node(int x, int y) { 5 this.x = x; 6 this.y = y; 7 } 8 } 9 public class Solution { 10 public boolean hasPath(int[][] maze, int[] start, int[] destination) { 11 if (maze == null || maze.length == 0 || maze[0].length == 0) { 12 return true; 13 } 14 if (start[0] == destination[0] && start[1] == destination[1]) { 15 return true; 16 } 17 int n = maze.length; 18 int m = maze[0].length; 19 Queue<Node> queue = new LinkedList<>(); 20 boolean[][] visited = new boolean[n][m]; 21 queue.offer(new Node(start[0], start[1])); 22 visited[start[0]][start[1]] = true; 23 int[] dx = {1, 0, -1, 0}; 24 int[] dy = {0, 1, 0, -1}; 25 while (!queue.isEmpty()) { 26 Node cur = queue.poll(); 27 for (int i = 0; i < 4; i++) { 28 // 这里的操作跟一般的BFS不同。 29 // 一般情况下BFS的步长是1,这里BFS的步长是由向某个方向一直走直到撞墙才能确定。 30 int next_x = cur.x; 31 int next_y = cur.y; 32 while (next_x >= 0 && next_x < n && next_y >= 0 && next_y < m 33 && maze[next_x][next_y] == 0) { 34 next_x += dx[i]; 35 next_y += dy[i]; 36 } 37 next_x -= dx[i]; 38 next_y -= dy[i]; 39 if (visited[next_x][next_y]) { 40 continue; 41 } 42 if (next_x == destination[0] && next_y == destination[1]) { 43 return true; 44 } 45 queue.offer(new Node(next_x, next_y)); 46 visited[next_x][next_y] = true; 47 } 48 } 49 return false; 50 } 51 }
505. The Maze II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling up, down, left or right, but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction. Given the ball's start position, the destination and the maze, find the shortest distance for the ball to stop at the destination. The distance is defined by the number of empty spaces traveled by the ball from the start position (excluded) to the destination (included). If the ball cannot stop at the destination, return -1. The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the maze are all walls. The start and destination coordinates are represented by row and column indexes. Example 1 Input 1: a maze represented by a 2D array 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 Input 2: start coordinate (rowStart, colStart) = (0, 4) Input 3: destination coordinate (rowDest, colDest) = (4, 4) Output: 12 Explanation: One shortest way is : left -> down -> left -> down -> right -> down -> right. The total distance is 1 + 1 + 3 + 1 + 2 + 2 + 2 = 12. Example 2 Input 1: a maze represented by a 2D array 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 Input 2: start coordinate (rowStart, colStart) = (0, 4) Input 3: destination coordinate (rowDest, colDest) = (3, 2) Output: -1 Explanation: There is no way for the ball to stop at the destination. Note: There is only one ball and one destination in the maze. Both the ball and the destination exist on an empty space, and they will not be at the same position initially. The given maze does not contain border (like the red rectangle in the example pictures), but you could assume the border of the maze are all walls. The maze contains at least 2 empty spaces, and both the width and height of the maze won't exceed 100.
题意:在I的基础上求最短路径,无路径返回-1。
思路:BFS。这道题相当于Dijkstra求最短路径。为了加快速度,使用PriorityQueue而不是普通的queue,每个节点三个属性:横坐标x,纵坐标y,到当前节点的路径长度l。再设置一个全局distance数组,distance[i][j]表示到(i,j)节点的最短路径。BFS结束后,返回distance[i][j](如果路径存在),否则返回-1。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 int l; 5 public Node(int x, int y, int l) { 6 this.x = x; 7 this.y = y; 8 this.l = l; 9 } 10 } 11 public class Solution { 12 public int shortestDistance(int[][] maze, int[] start, int[] destination) { 13 if (maze == null || maze.length == 0 || maze[0].length == 0) { 14 return 0; 15 } 16 if (start[0] == destination[0] && start[1] == destination[1]) { 17 return 0; 18 } 19 int n = maze.length; 20 int m = maze[0].length; 21 PriorityQueue<Node> pq = new PriorityQueue<>(new Comparator<Node>() { 22 public int compare(Node a, Node b) { 23 return a.l - b.l; 24 } 25 }); 26 int[][] distance = new int[n][m]; 27 pq.offer(new Node(start[0], start[1], 0)); 28 for (int i = 0; i < n; i++) { 29 for (int j = 0; j < m; j++) { 30 distance[i][j] = Integer.MAX_VALUE; 31 } 32 } 33 int[] dx = {1, 0, -1, 0}; 34 int[] dy = {0, 1, 0, -1}; 35 while (!pq.isEmpty()) { 36 Node cur = pq.poll(); 37 if (cur.l >= distance[cur.x][cur.y]) { 38 continue; 39 } 40 distance[cur.x][cur.y] = cur.l; 41 for (int i = 0; i < 4; i++) { 42 int next_x = cur.x; 43 int next_y = cur.y; 44 int l = cur.l; 45 while (next_x >= 0 && next_x < n && next_y >= 0 && next_y < m 46 && maze[next_x][next_y] != 1) { 47 next_x += dx[i]; 48 next_y += dy[i]; 49 l++; 50 } 51 next_x -= dx[i]; 52 next_y -= dy[i]; 53 l--; 54 pq.offer(new Node(next_x, next_y, l)); 55 } 56 } 57 if (distance[destination[0]][destination[1]] == Integer.MAX_VALUE) { 58 return -1; 59 } 60 return distance[destination[0]][destination[1]]; 61 } 62 }
499. The Maze III
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling up (u), down (d), left (l) or right (r), but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction. There is also a hole in this maze. The ball will drop into the hole if it rolls on to the hole. Given the ball position, the hole position and the maze, find out how the ball could drop into the hole by moving the shortest distance. The distance is defined by the number of empty spaces traveled by the ball from the start position (excluded) to the hole (included). Output the moving directions by using 'u', 'd', 'l' and 'r'. Since there could be several different shortest ways, you should output the lexicographically smallest way. If the ball cannot reach the hole, output "impossible". The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the maze are all walls. The ball and the hole coordinates are represented by row and column indexes. Example 1 Input 1: a maze represented by a 2D array 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 Input 2: ball coordinate (rowBall, colBall) = (4, 3) Input 3: hole coordinate (rowHole, colHole) = (0, 1) Output: "lul" Explanation: There are two shortest ways for the ball to drop into the hole. The first way is left -> up -> left, represented by "lul". The second way is up -> left, represented by 'ul'. Both ways have shortest distance 6, but the first way is lexicographically smaller because 'l' < 'u'. So the output is "lul". Example 2 Input 1: a maze represented by a 2D array 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 Input 2: ball coordinate (rowBall, colBall) = (4, 3) Input 3: hole coordinate (rowHole, colHole) = (3, 0) Output: "impossible" Explanation: The ball cannot reach the hole. Note: There is only one ball and one hole in the maze. Both the ball and hole exist on an empty space, and they will not be at the same position initially. The given maze does not contain border (like the red rectangle in the example pictures), but you could assume the border of the maze are all walls. The maze contains at least 2 empty spaces, and the width and the height of the maze won't exceed 30.
题意:球进洞的路径上的方向。
思路:类似II,多一个洞的位置的判断,比较的时候是距离l和字典顺序两个因素的比较。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 int l; 5 String s; 6 public Node(int x, int y, int l, String s) { 7 this.x = x; 8 this.y = y; 9 this.l = l; 10 this.s = s; 11 } 12 public int compareTo(Node node) { 13 if (l != node.l) { 14 return l - node.l; 15 } else { 16 return s.compareTo(node.s); 17 } 18 } 19 } 20 public class Solution { 21 public String findShortestWay(int[][] maze, int[] ball, int[] hole) { 22 if (maze == null || maze.length == 0 || maze[0].length == 0) { 23 return ""; 24 } 25 if (ball[0] == hole[0] && ball[1] == hole[1]) { 26 return ""; 27 } 28 int n = maze.length; 29 int m = maze[0].length; 30 String[] directions = {"d","r","u","l"}; 31 Node[][] nodes = new Node[n][m]; 32 for (int i = 0; i < n; i++) { 33 for (int j = 0; j < m; j++) { 34 nodes[i][j] = new Node(i, j, Integer.MAX_VALUE, ""); 35 } 36 } 37 PriorityQueue<Node> pq = new PriorityQueue<>(new Comparator<Node>() { 38 public int compare(Node a, Node b) { 39 if (a.l != b.l) { 40 return a.l - b.l; 41 } else { 42 return a.s.compareTo(b.s); 43 } 44 } 45 }); 46 pq.offer(new Node(ball[0], ball[1], 0, "")); 47 int[] dx = {1, 0, -1, 0}; 48 int[] dy = {0, 1, 0, -1}; 49 while (!pq.isEmpty()) { 50 Node cur = pq.poll(); 51 if (nodes[cur.x][cur.y].compareTo(cur) <= 0) { 52 continue; 53 } 54 // if (nodes[cur.x][cur.y].l < cur.l) { 55 // continue; 56 // } 57 // if (nodes[cur.x][cur.y].l == cur.l && nodes[cur.x][cur.y].s.compareTo(cur.s) <= 0) { 58 // continue; 59 // } 60 nodes[cur.x][cur.y] = cur; 61 for (int i = 0; i < 4; i++) { 62 int next_x = cur.x; 63 int next_y = cur.y; 64 int next_l = cur.l; 65 while (next_x >= 0 && next_x < n && next_y >= 0 && next_y < m 66 && maze[next_x][next_y] == 0 && (next_x != hole[0] || next_y != hole[1])) { 67 next_x += dx[i]; 68 next_y += dy[i]; 69 next_l++; 70 } 71 if (next_x != hole[0] || next_y != hole[1]) { 72 next_x -= dx[i]; 73 next_y -= dy[i]; 74 next_l--; 75 } 76 pq.offer(new Node(next_x, next_y, next_l, cur.s + directions[i])); 77 } 78 } 79 if (nodes[hole[0]][hole[1]].l == Integer.MAX_VALUE) { 80 return "impossible"; 81 } 82 return nodes[hole[0]][hole[1]].s; 83 } 84 }
199. Binary Tree Right Side View
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom. For example: Given the following binary tree, 1 <--- / \ 2 3 <--- \ \ 5 4 <--- You should return [1, 3, 4].
题意:求二叉树从右看每一层的值
思路:BFS,分层,每一层中最后一个节点加入到结果集中。或者逆序二叉树层序遍历,每一层中第一个节点加入到结果集中。时间复杂度O(n),空间复杂度O(1),n为二叉树节点个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom. 2 3 For example: 4 Given the following binary tree, 5 1 <--- 6 / \ 7 2 3 <--- 8 \ \ 9 5 4 <--- 10 You should return [1, 3, 4].
107. Binary Tree Level Order Traversal II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root). For example: Given binary tree [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 return its bottom-up level order traversal as: [ [15,7], [9,20], [3] ]
题意:二叉树层序遍历,每一层元素顺序反转。
思路:BFS,分层,每一层的元素用tempList收集,不同的地方在于加入到结果集时用list.add(int index, List<Integer> tempList)来实现每层元素集合的反转。时间复杂度O(n),空间复杂度O(1),n为二叉树节点个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public List<List<Integer>> levelOrderBottom(TreeNode root) { 12 List<List<Integer>> result = new ArrayList<>(); 13 if (root == null) { 14 return result; 15 } 16 Queue<TreeNode> queue = new LinkedList<>(); 17 queue.offer(root); 18 while (!queue.isEmpty()) { 19 List<Integer> tempList = new ArrayList<>(); 20 int size = queue.size(); 21 for (int i = 0; i < size; i++) { 22 TreeNode node = queue.poll(); 23 tempList.add(node.val); 24 if (node.left != null) { 25 queue.offer(node.left); 26 } 27 if (node.right != null) { 28 queue.offer(node.right); 29 } 30 } 31 result.add(0, tempList); 32 } 33 return result; 34 } 35 }
102. Binary Tree Level Order Traversal
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). For example: Given binary tree [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 return its level order traversal as: [ [3], [9,20], [15,7] ]
题意:二叉树层序遍历
思路:BFS,分层,so easy,练好bug free。时间复杂度O(n),空间复杂度O(n),n为节点总个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public List<List<Integer>> levelOrder(TreeNode root) { 12 List<List<Integer>> result = new ArrayList<>(); 13 if (root == null) { 14 return result; 15 } 16 Queue<TreeNode> queue = new LinkedList<>(); 17 queue.offer(root); 18 while (!queue.isEmpty()) { 19 List<Integer> tempList = new ArrayList<>(); 20 int size = queue.size(); 21 for (int i = 0; i < size; i++) { 22 TreeNode node = queue.poll(); 23 tempList.add(node.val); 24 if (node.left != null) { 25 queue.offer(node.left); 26 } 27 if (node.right != null) { 28 queue.offer(node.right); 29 } 30 } 31 result.add(tempList); 32 } 33 return result; 34 } 35 }
101. Symmetric Tree
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For example, this binary tree [1,2,2,3,4,4,3] is symmetric: 1 / \ 2 2 / \ / \ 3 4 4 3 But the following [1,2,2,null,3,null,3] is not: 1 / \ 2 2 \ \ 3 3 Note: Bonus points if you could solve it both recursively and iteratively.
题目:判断一棵二叉树是不是对称树
思路:DFS。对于一个节点,看以其两个子节点开始是否为对称树,有以下几种情况:
1. left==null && right == null 此时为对称树,返回true
2. left和right一个null一个非null 此时不为对称树,返回false
3. left非null && right非null,此时分两种情况:
(1)left节点值不等于right节点值,此时不为对称树,返回false
(2)left节点值不等于right节点值,此时再递归看left.left与right.right是否构成对称树
以及left.right与right.left是否构成称树
时间复杂度O(n),n为节点总个数,空间复杂度O(lgn),即递归深度。
这道题是剑指offer 面试题59,事实上这道题就是比较二叉树前序遍历(遍历顺序是:中左右)和对称前序遍历(遍历顺序是:中右左)的序列是否相同,注意null节点也要考虑进来!理解了这个代码就不难理解,就是DFS同时进行前序遍历和对称前序遍历,比较当前两个节点的值(如果两个节点都非null)是否相同。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public boolean isSymmetric(TreeNode root) { 12 if (root == null) { 13 return true; 14 } 15 return helper(root.left, root.right); 16 } 17 public boolean helper(TreeNode left, TreeNode right) { 18 if (left == null && right == null) { 19 return true; 20 } 21 if (left == null || right == null) { 22 return false; 23 } 24 if (left.val != right.val) { 25 return false; 26 } 27 return helper(left.left, right.right) && helper(left.right, right.left); 28 } 29 }
261. Graph Valid Tree
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree. For example: Given n = 5 and edges = [[0, 1], [0, 2], [0, 3], [1, 4]], return true. Given n = 5 and edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]], return false. Note: you can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.
题意:判断图是不是树
思路I:判断条件有两个:1. 顶点数=边数-1 2. 图中没有环路
并查集。首先判断条件1,再看条件2,一旦出现一条边的两端节点的父亲节点相同,则说明图中有环路,返回false,边遍历结束都没有发现环路说明图是树,返回true。时间复杂度O(e),e为图的边数,空间复杂度O(n),n为节点个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class UnionFind { 2 int[] father; 3 public UnionFind(int n) { 4 father = new int[n]; 5 for (int i = 0; i < n; i++) { 6 father[i] = i; 7 } 8 } 9 public int find(int x) { 10 if (father[x] == x) { 11 return x; 12 } 13 return father[x] = find(father[x]); 14 } 15 public void union(int a, int b) { 16 int root_a = find(a); 17 int root_b = find(b); 18 if (root_a != root_b) { 19 father[root_a] = root_b; 20 } 21 } 22 } 23 public class Solution { 24 public boolean validTree(int n, int[][] edges) { 25 if (edges == null) { 26 if (n == 1) { 27 return true; 28 } else { 29 return false; 30 } 31 } 32 if (n - 1 != edges.length) { 33 return false; 34 } 35 UnionFind uf = new UnionFind(n); 36 for (int i = 0; i < edges.length; i++) { 37 if (uf.find(edges[i][0]) == uf.find(edges[i][1])) { 38 return false; 39 } 40 uf.union(edges[i][0], edges[i][1]); 41 } 42 return true; 43 } 44 }
思路II:BFS。先判断条件1,再看从节点0(任一节点都可以)开始BFS遍历过的节点数是否等于节点总数n。时间复杂度O(e),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean validTree(int n, int[][] edges) { 3 if (edges == null) { 4 if (n == 1) { 5 return true; 6 } else { 7 return false; 8 } 9 } 10 if (n - 1 != edges.length) { 11 return false; 12 } 13 Map<Integer, Set<Integer>> graph = initializeGraph(n, edges); 14 Queue<Integer> queue = new LinkedList<>(); 15 Set<Integer> set = new HashSet<>(); 16 queue.offer(0); 17 set.add(0); 18 while (!queue.isEmpty()) { 19 int num = queue.poll(); 20 for (int neighbor : graph.get(num)) { 21 if (!set.contains(neighbor)) { 22 queue.offer(neighbor); 23 set.add(neighbor); 24 } 25 } 26 } 27 return set.size() == n; 28 } 29 public Map<Integer, Set<Integer>> initializeGraph(int n, int[][] edges) { 30 Map<Integer, Set<Integer>> graph = new HashMap<>(); 31 for (int i = 0; i < n; i++) { 32 graph.put(i, new HashSet<Integer>()); 33 } 34 for (int i = 0; i < edges.length; i++) { 35 graph.get(edges[i][0]).add(edges[i][1]); 36 graph.get(edges[i][1]).add(edges[i][0]); 37 } 38 return graph; 39 } 40 }
42. Trapping Rain Water
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining. For example, Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.
题意:一维矩阵能装水的容积
思路:双指针。并且定义左右两个边界高度leftMax和rightMax,当前位置能装水的体积 = Min(leftMax, rightMax) - 当前高度。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int trap(int[] height) { 3 if (height == null || height.length == 0) { 4 return 0; 5 } 6 int left = 0; 7 int right = height.length - 1; 8 int leftMax = 0; 9 int rightMax = 0; 10 int sum = 0; 11 while (left <= right) { 12 if (height[left] <= height[right]) { 13 if (height[left] >= leftMax) { 14 leftMax = height[left]; 15 } else { 16 sum += leftMax - height[left]; 17 } 18 left++; 19 } else { 20 if (height[right] >= rightMax) { 21 rightMax = height[right]; 22 } else { 23 sum += rightMax - height[right]; 24 } 25 right--; 26 } 27 } 28 return sum; 29 } 30 }
407. Trapping Rain Water II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an m x n matrix of positive integers representing the height of each unit cell in a 2D elevation map, compute the volume of water it is able to trap after raining. Note: Both m and n are less than 110. The height of each unit cell is greater than 0 and is less than 20,000. Example: Given the following 3x6 height map: [ [1,4,3,1,3,2], [3,2,1,3,2,4], [2,3,3,2,3,1] ] Return 4.
题意:二维矩阵能装水的容积
思路:从二维矩阵边界开始,将节点加入到优先队列中,然后BFS遍历一遍二维矩阵。如果下一个节点高度小于当前节点高度,这个高度差就是当前积水体积,否则,没有积水。注意下一个节点的高度属性应该取当前节点的高度和下一个节点高度中的最大值,即边界高度始终是递增的,类似于Trapping Rain Water I中边界高度都是递增的。时间复杂度O(nm),空间复杂度O(nm),n为行数,m为列数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 int h; 5 public Node(int x, int y, int h) { 6 this.x = x; 7 this.y = y; 8 this.h = h; 9 } 10 } 11 public class Solution { 12 public int trapRainWater(int[][] heightMap) { 13 if (heightMap == null || heightMap.length == 0 || heightMap[0].length == 0) { 14 return 0; 15 } 16 int n = heightMap.length; 17 int m = heightMap[0].length; 18 PriorityQueue<Node> pq = new PriorityQueue<>(new Comparator<Node>() { 19 public int compare(Node a, Node b) { 20 return a.h - b.h; 21 } 22 }); 23 boolean[][] visited = new boolean[n][m]; 24 for (int i = 0; i < n; i++) { 25 pq.offer(new Node(i, 0, heightMap[i][0])); 26 visited[i][0] = true; 27 pq.offer(new Node(i, m - 1, heightMap[i][m - 1])); 28 visited[i][m - 1] = true; 29 } 30 for (int i = 0; i < m; i++) { 31 pq.offer(new Node(0, i, heightMap[0][i])); 32 visited[0][i] = true; 33 pq.offer(new Node(n - 1, i, heightMap[n - 1][i])); 34 visited[n - 1][i] = true; 35 } 36 int[] dx = {1, 0, -1, 0}; 37 int[] dy = {0, 1, 0, -1}; 38 int sum = 0; 39 while (!pq.isEmpty()) { 40 Node cur = pq.poll(); 41 for (int i = 0; i < 4; i++) { 42 int next_x = cur.x + dx[i]; 43 int next_y = cur.y + dy[i]; 44 if (next_x >= 0 && next_x < n && next_y >= 0 && next_y < m && !visited[next_x][next_y]){ 45 Node next = new Node(next_x, next_y, heightMap[next_x][next_y]); 46 if (next.h < cur.h) { 47 sum += cur.h - next.h; 48 next.h = cur.h; 49 } 50 pq.offer(next); 51 visited[next.x][next.y] = true; 52 } 53 } 54 } 55 return sum; 56 } 57 }
279. Perfect Squares
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n. For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.
题意:n由完全平方数求和得到的最少完全平方数的个数
思路:DP。
1. 状态定义 dp[n]表示由完全平方数求和为n的最少完全平方数的个数
2. 初始化 dp[i*i] = 1, 其他dp[j] = Integer.MAX_VALUE
3. 状态方程 for i: 0 ~ n
for j: 0 ~ i + j * j <= n
dp[i + j * j] =Math.min(dp[i + j * j], dp[i] + 1)
4. 返回结果 dp[n]
时间复杂度O(nlgn),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int numSquares(int n) { 3 if (n <= 0) { 4 return 0; 5 } 6 // 1 状态定义 7 int[] dp = new int[n + 1]; 8 // 2 初始化 9 Arrays.fill(dp, Integer.MAX_VALUE); 10 for (int i = 0; i * i <= n; i++) { 11 dp[i * i] = 1; 12 } 13 // 3 循环递归求解 14 for (int i = 0; i <= n; i++) { 15 for (int j = 0; i + j * j <= n; j++) { 16 dp[i + j * j] = Math.min(dp[i + j * j], dp[i] + 1); 17 } 18 } 19 // 4 返回结果 20 return dp[n]; 21 } 22 }
301. Remove Invalid Parentheses
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results. Note: The input string may contain letters other than the parentheses ( and ). Examples: "()())()" -> ["()()()", "(())()"] "(a)())()" -> ["(a)()()", "(a())()"] ")(" -> [""]
题意:去掉无效括号,求所有可能结果。
思路:DFS。先去掉多余的')',再反转s去掉多余的'(',此时可以重用代码,只需将()变成)(就可以重用。
对于一个字符串,在任何时候如果')'的个数多于左括号,则说明从开始到现在位置必然可以删除一个')'.而这段子串可能包含多个')',删除哪一个呢?当然删除任何一个都可以.例如对于()())(),从开头到s[4]位置构成的子串多了一个右括号,因此我们需要删掉一个,而这个子串有三个右括号,但是只会产生2个结果,也就是会有一个重复值.所以在删除括号的时候,为保证不会产生重复值,需要记录一个最后删除的位置,这样可以使得在接下来删除的时候只删除这个位置之后的值.这样我们可以使得当前这一段子串不再包含多余的右括号了.这样我们可以删除了一个右括号之后合法的子串与后面还没有检查过的子串组成一个新的字符串重新开始检查.直到不再含有非法的右括号.
但是还有一种情况是包含了多余的左括号,一种直观的方法是从右向左再按照上面的方法处理一遍左括号.但是将数组逆置之后就可以重用上面的算法了.
所以总的思路就是先对字符串进行处理使得其不再含有非法右括号,然后将其翻转以后再检查是否含有非法的左括号.最后左右括号都检查完之后都合法就是我们要的答案了.
时间复杂度应该是O(nk),n为字符串长度,k为答案个数。每次递归只会产生正确的结果。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<String> removeInvalidParentheses(String s) { 3 List<String> result = new ArrayList<>(); 4 remove(s, 0, 0, new char[]{'(', ')'}, result); 5 return result; 6 } 7 public void remove(String s, int last_i, int last_j, char[] par, List<String> result) { 8 int count = 0; 9 for (int i = last_i; i < s.length(); i++) { 10 if (s.charAt(i) == par[0]) { 11 count++; 12 } 13 if (s.charAt(i) == par[1]) { 14 count--; 15 } 16 if (count >= 0) { 17 continue; 18 } 19 for (int j = last_j; j <= i; j++) { 20 if (s.charAt(j) == par[1] && (j == last_j || s.charAt(j - 1) != par[1])) { 21 remove(s.substring(0, j) + s.substring(j + 1, s.length()), i, j, par, result); 22 } 23 } 24 return; 25 } 26 String reversed = new StringBuilder(s).reverse().toString(); 27 if (par[0] == '(') { 28 remove(reversed, 0, 0, new char[]{')', '('}, result); 29 } else { 30 result.add(reversed); 31 } 32 } 33 }
200. Number of Islands
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water. Example 1: 11110 11010 11000 00000 Answer: 1 Example 2: 11000 11000 00100 00011 Answer: 3
思路I:并查集
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class UnionFind { 2 private int[] father; 3 private int count; 4 public UnionFind(int n, int count) { 5 father = new int[n]; 6 for (int i = 0; i < n; i++) { 7 father[i] = i; 8 } 9 this.count = count; 10 } 11 public int find(int x) { 12 if (father[x] == x) { 13 return x; 14 } 15 return father[x] = find(father[x]); 16 } 17 public void union(int a, int b) { 18 int root_a = find(a); 19 int root_b = find(b); 20 if (root_a != root_b) { 21 father[root_a] = root_b; 22 count--; 23 } 24 } 25 public int query() { 26 return count; 27 } 28 } 29 public class Solution { 30 public int numIslands(char[][] grid) { 31 if (grid == null || grid.length == 0 || grid[0].length == 0) { 32 return 0; 33 } 34 int n = grid.length; 35 int m = grid[0].length; 36 int count = 0; 37 for (int i = 0; i < n; i++) { 38 for (int j = 0; j < m; j++) { 39 if (grid[i][j] == '1') { 40 count++; 41 } 42 } 43 } 44 UnionFind uf = new UnionFind(n * m, count); 45 for (int i = 0; i < n; i++) { 46 for (int j = 0; j < m; j++) { 47 if (grid[i][j] == '1') { 48 if (i > 0 && grid[i - 1][j] == '1') { 49 uf.union((i - 1) * m + j, i * m + j); 50 } 51 if (j > 0 && grid[i][j - 1] == '1') { 52 uf.union(i * m + j - 1, i * m + j); 53 } 54 if (j < m - 1 && grid[i][j + 1] == '1') { 55 uf.union(i * m + j + 1, i * m + j); 56 } 57 if (i < n - 1 && grid[i + 1][j] == '1') { 58 uf.union((i + 1) * m + j, i * m + j); 59 } 60 } 61 } 62 } 63 return uf.query(); 64 } 65 }
思路II:BFS
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 public Node(int x, int y) { 5 this.x = x; 6 this.y = y; 7 } 8 } 9 public class Solution { 10 int n; 11 int m; 12 public int numIslands(char[][] grid) { 13 if (grid == null || grid.length == 0 || grid[0].length == 0) { 14 return 0; 15 } 16 n = grid.length; 17 m = grid[0].length; 18 int islands = 0; 19 for (int i = 0; i < n; i++) { 20 for (int j = 0; j < m; j++) { 21 if (grid[i][j] == '1') { 22 bfs(grid, i, j); 23 islands++; 24 } 25 } 26 } 27 return islands; 28 } 29 public void bfs(char[][] grid, int x, int y) { 30 Queue<Node> queue = new LinkedList<>(); 31 queue.offer(new Node(x, y)); 32 grid[x][y] = '0'; 33 int[] dx = {1, 0, -1, 0}; 34 int[] dy = {0, 1, 0, -1}; 35 while (!queue.isEmpty()) { 36 Node cur = queue.poll(); 37 for (int i = 0; i < 4; i++) { 38 int next_x = cur.x + dx[i]; 39 int next_y = cur.y + dy[i]; 40 if (next_x >= 0 && next_x < n && next_y >= 0 && next_y < m 41 && grid[next_x][next_y] == '1') { 42 queue.offer(new Node(next_x, next_y)); 43 grid[next_x][next_y] = '0'; 44 } 45 } 46 } 47 } 48 }
103. Binary Tree Zigzag Level Order Traversal
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between). For example: Given binary tree [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 return its zigzag level order traversal as: [ [3], [20,9], [15,7] ]
思路:BFS。用一个flag记录当前层是奇数层还是偶数层即可,比如flag=0代表第1、3、5...层,flag=1代表第2、4、6层。时间复杂度O(n),空间复杂度O(n)。一次AC
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public List<List<Integer>> zigzagLevelOrder(TreeNode root) { 12 List<List<Integer>> result = new ArrayList<>(); 13 if (root == null) { 14 return result; 15 } 16 Queue<TreeNode> queue = new LinkedList<>(); 17 queue.offer(root); 18 int flag = 0; 19 while (!queue.isEmpty()) { 20 List<Integer> tempList = new ArrayList<>(); 21 int size = queue.size(); 22 for (int i = 0; i < size; i++) { 23 TreeNode node = queue.poll(); 24 if (flag == 0) { 25 tempList.add(node.val); 26 } else { 27 tempList.add(0, node.val); 28 } 29 if (node.left != null) { 30 queue.offer(node.left); 31 } 32 if (node.right != null) { 33 queue.offer(node.right); 34 } 35 } 36 result.add(tempList); 37 flag = 1 - flag; 38 } 39 return result; 40 } 41 }
417. Pacific Atlantic Water Flow
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an m x n matrix of non-negative integers representing the height of each unit cell in a continent, the "Pacific ocean" touches the left and top edges of the matrix and the "Atlantic ocean" touches the right and bottom edges. Water can only flow in four directions (up, down, left, or right) from a cell to another one with height equal or lower. Find the list of grid coordinates where water can flow to both the Pacific and Atlantic ocean. Note: The order of returned grid coordinates does not matter. Both m and n are less than 150. Example: Given the following 5x5 matrix: Pacific ~ ~ ~ ~ ~ ~ 1 2 2 3 (5) * ~ 3 2 3 (4) (4) * ~ 2 4 (5) 3 1 * ~ (6) (7) 1 4 5 * ~ (5) 1 1 2 4 * * * * * * Atlantic Return: [[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (positions with parentheses in above matrix).
思路:从海洋边界出发,逆向BFS,这个过程所访问到的节点就是能够到达海洋的节点。两次BFS过后,取能同时到达两个海洋的节点加入到结果中。时间复杂度O(nm),空间复杂度O(nm),n为行数,m为列数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 public Node(int x, int y) { 5 this.x = x; 6 this.y = y; 7 } 8 } 9 public class Solution { 10 int n; 11 int m; 12 public List<int[]> pacificAtlantic(int[][] matrix) { 13 List<int[]> result = new ArrayList<>(); 14 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 15 return result; 16 } 17 n = matrix.length; 18 m = matrix[0].length; 19 Queue<Node> queue = new LinkedList<>(); 20 boolean[][] pacific = new boolean[n][m]; 21 for (int i = 0; i < n; i++) { 22 queue.offer(new Node(i, 0)); 23 pacific[i][0] = true; 24 } 25 for (int i = 1; i < m; i++) { 26 queue.offer(new Node(0, i)); 27 pacific[0][i] = true; 28 } 29 bfs(matrix, queue, pacific); 30 boolean[][] atlantic = new boolean[n][m]; 31 for (int i = 0; i < n; i++) { 32 queue.offer(new Node(i, m - 1)); 33 atlantic[i][m - 1] = true; 34 } 35 for (int i = 0; i < m - 1; i++) { 36 queue.offer(new Node(n - 1, i)); 37 atlantic[n - 1][i] = true; 38 } 39 bfs(matrix, queue, atlantic); 40 for (int i = 0; i < n; i++) { 41 for (int j = 0; j < m; j++) { 42 if (pacific[i][j] && atlantic[i][j]) { 43 result.add(new int[]{i, j}); 44 } 45 } 46 } 47 return result; 48 } 49 public void bfs(int[][] matrix, Queue<Node> queue, boolean[][] visited) { 50 int[] dx = {1, 0, -1, 0}; 51 int[] dy = {0, 1, 0, -1}; 52 while (!queue.isEmpty()) { 53 Node cur = queue.poll(); 54 for (int i = 0; i < 4; i++) { 55 int next_x = cur.x + dx[i]; 56 int next_y = cur.y + dy[i]; 57 if (next_x >= 0 && next_x < n && next_y >= 0 && next_y < m 58 && !visited[next_x][next_y] && matrix[next_x][next_y] >= matrix[cur.x][cur.y]) { 59 queue.offer(new Node(next_x, next_y)); 60 visited[next_x][next_y] = true; 61 } 62 } 63 } 64 } 65 }
111. Minimum Depth of Binary Tree
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a binary tree, find its minimum depth.
The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
思路I:DFS。DFS过程要记录当前递归深度,遍历到叶节点返回,每次遇到叶节点,用当前深度更新minimum。时间复杂度O(n),空间复杂度O(lgn),n为节点个数。一次AC
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 int minimum = Integer.MAX_VALUE; 12 public int minDepth(TreeNode root) { 13 if (root == null) { 14 return 0; 15 } 16 dfs(root, 1); 17 return minimum; 18 } 19 public void dfs(TreeNode root, int depth) { 20 if (root.left == null && root.right == null) { 21 minimum = Math.min(minimum, depth); 22 return; 23 } 24 if (root.left != null) { 25 dfs(root.left, depth + 1); 26 } 27 if (root.right != null) { 28 dfs(root.right, depth + 1); 29 } 30 } 31 }
思路II:Divide Conquer。注意当当前节点的左右子节点有一个为null的情况,这个时候深度的计算有所不同。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public int minDepth(TreeNode root) { 12 if (root == null) { 13 return 0; 14 } 15 int left = minDepth(root.left); 16 int right = minDepth(root.right); 17 if (left == 0 || right == 0) { 18 return 1 + left + right; 19 } else { 20 return 1 + Math.min(left, right); 21 } 22 } 23 }
542. 01 Matrix
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 Given a matrix consists of 0 and 1, find the distance of the nearest 0 for each cell. 2 3 The distance between two adjacent cells is 1. 4 Example 1: 5 Input: 6 7 0 0 0 8 0 1 0 9 0 0 0 10 Output: 11 0 0 0 12 0 1 0 13 0 0 0 14 Example 2: 15 Input: 16 17 0 0 0 18 0 1 0 19 1 1 1 20 Output: 21 0 0 0 22 0 1 0 23 1 2 1 24 Note: 25 The number of elements of the given matrix will not exceed 10,000. 26 There are at least one 0 in the given matrix. 27 The cells are adjacent in only four directions: up, down, left and right.
思路:BFS。相当于从0开始一层层往里面遍历,下一个节点的距离最近0的距离就是当前节点的距离+1。这道题BFS分层不分层都可以。时间复杂度O(nm),空间复杂度O(nm),n为行数,m为列数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 public Node(int x, int y) { 5 this.x = x; 6 this.y = y; 7 } 8 } 9 public class Solution { 10 public int[][] updateMatrix(int[][] matrix) { 11 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 12 return matrix; 13 } 14 int n = matrix.length; 15 int m = matrix[0].length; 16 Queue<Node> queue = new LinkedList<>(); 17 boolean[][] visited = new boolean[n][m]; 18 for (int i = 0; i < n; i++) { 19 for (int j = 0; j < m; j++) { 20 if (matrix[i][j] == 0) { 21 queue.offer(new Node(i, j)); 22 visited[i][j] = true; 23 } 24 } 25 } 26 int[] dx = {1, 0, -1, 0}; 27 int[] dy = {0, 1, 0, -1}; 28 while (!queue.isEmpty()) { 29 // 这道题分层不分层都可以 30 int size = queue.size(); 31 for (int i = 0; i < size; i++) { 32 Node cur = queue.poll(); 33 for (int j = 0; j < 4; j++) { 34 int next_x = cur.x + dx[j]; 35 int next_y = cur.y + dy[j]; 36 if (next_x >= 0 && next_x < n && next_y >= 0 && next_y < m 37 && !visited[next_x][next_y]) { 38 queue.offer(new Node(next_x, next_y)); 39 matrix[next_x][next_y] = matrix[cur.x][cur.y] + 1; 40 visited[next_x][next_y] = true; 41 } 42 } 43 } 44 } 45 return matrix; 46 } 47 }
317. Shortest Distance from All Buildings
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
You want to build a house on an empty land which reaches all buildings in the shortest amount of distance. You can only move up, down, left and right. You are given a 2D grid of values 0, 1 or 2, where: Each 0 marks an empty land which you can pass by freely. Each 1 marks a building which you cannot pass through. Each 2 marks an obstacle which you cannot pass through. For example, given three buildings at (0,0), (0,4), (2,2), and an obstacle at (0,2): 1 - 0 - 2 - 0 - 1 | | | | | 0 - 0 - 0 - 0 - 0 | | | | | 0 - 0 - 1 - 0 - 0 The point (1,2) is an ideal empty land to build a house, as the total travel distance of 3+3+1=7 is minimal. So return 7. Note: There will be at least one building. If it is not possible to build such house according to the above rules, return -1.
思路:BFS。对每一个1开始进行分层BFS,所得的到每一个值为0的点的距离累加,到值为0的1的个数累加。BFS结束后,得到两个二维矩阵,distance[i][j]表示该位置到所有能到达的建筑物的距离之和,reach[i][j]表示该位置能够到达的建筑物的个数,只有当reach[i][j]==建筑物总数时,distance[i][j]才能表示该位置到达所有建筑物的距离之和,用这个值更新全局最短距离shortest。时间复杂度O(n^2*m^2),空间复杂度O(nm),n为行数,m为列数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 public Node(int x, int y) { 5 this.x = x; 6 this.y = y; 7 } 8 } 9 public class Solution { 10 int n; 11 int m; 12 public int shortestDistance(int[][] grid) { 13 if (grid == null || grid.length == 0 || grid[0].length == 0) { 14 return 0; 15 } 16 n = grid.length; 17 m = grid[0].length; 18 int buildings = 0; 19 Queue<Node> queue = new LinkedList<>(); 20 int[][] distance = new int[n][m]; 21 int[][] reach = new int[n][m]; 22 for (int i = 0; i < n; i++) { 23 for (int j = 0; j < m; j++) { 24 if (grid[i][j] == 1) { 25 buildings++; 26 boolean[][] visited = new boolean[n][m]; 27 bfs(grid, i, j, queue, visited, distance, reach); 28 } 29 } 30 } 31 int shortest = Integer.MAX_VALUE; 32 for (int i = 0; i < n; i++) { 33 for (int j = 0; j < m; j++) { 34 if (grid[i][j] == 0) { 35 if (reach[i][j] == buildings && distance[i][j] < shortest) { 36 shortest = distance[i][j]; 37 } 38 } 39 } 40 } 41 if (shortest == Integer.MAX_VALUE) { 42 return -1; 43 } 44 return shortest; 45 } 46 public void bfs(int[][] grid, int x, int y, Queue<Node> queue, boolean[][] visited, 47 int[][] distance, int[][] reach) { 48 int[] dx = {1, 0, -1, 0}; 49 int[] dy = {0, 1, 0, -1}; 50 queue.offer(new Node(x, y)); 51 visited[x][y] = true; 52 int steps = 1; 53 while (!queue.isEmpty()) { 54 int size = queue.size(); 55 for (int i = 0; i < size; i++) { 56 Node cur = queue.poll(); 57 for (int j = 0; j < 4; j++) { 58 int next_x = cur.x + dx[j]; 59 int next_y = cur.y + dy[j]; 60 if (next_x >= 0 && next_x < n && next_y >= 0 && next_y < m 61 && !visited[next_x][next_y] && grid[next_x][next_y] == 0) { 62 distance[next_x][next_y] += steps; 63 reach[next_x][next_y]++; 64 queue.offer(new Node(next_x, next_y)); 65 visited[next_x][next_y] = true; 66 } 67 } 68 } 69 steps++; 70 } 71 } 72 }
207. Course Schedule
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
There are a total of n courses you have to take, labeled from 0 to n - 1. Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1] Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses? For example: 2, [[1,0]] There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible. 2, [[1,0],[0,1]] There are a total of 2 courses to take. To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible. Note: The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented. You may assume that there are no duplicate edges in the input prerequisites.
思路:拓扑排序。建立一个inDegree数组存储每个节点的度数,建立一个edges数组存储每个节点的邻居节点。最后判断统计的度为0的节点个数是否为n。时间复杂度O(n),空间复杂度O(n^2),n为节点个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean canFinish(int numCourses, int[][] prerequisites) { 3 if (numCourses < 1) { 4 return true; 5 } 6 int[] inDegree = new int[numCourses]; 7 List[] edges = new ArrayList[numCourses]; 8 for (int i = 0; i < numCourses; i++) { 9 edges[i] = new ArrayList<Integer>(); 10 } 11 for (int i = 0; i < prerequisites.length; i++) { 12 inDegree[prerequisites[i][0]]++; 13 edges[prerequisites[i][1]].add(prerequisites[i][0]); 14 } 15 Queue<Integer> queue = new LinkedList<>(); 16 int count = 0; 17 for (int i = 0; i < numCourses; i++) { 18 if (inDegree[i] == 0) { 19 queue.offer(i); 20 count++; 21 } 22 } 23 while (!queue.isEmpty()) { 24 int num = queue.poll(); 25 int size = edges[num].size(); 26 for (int i = 0; i < size; i++) { 27 int neighbor = (int) edges[num].get(i); 28 inDegree[neighbor]--; 29 if (inDegree[neighbor] == 0) { 30 queue.offer(neighbor); 31 count++; 32 } 33 } 34 } 35 return count == numCourses; 36 } 37 }
210. Course Schedule II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
There are a total of n courses you have to take, labeled from 0 to n - 1. Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1] Given the total number of courses and a list of prerequisite pairs, return the ordering of courses you should take to finish all courses. There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array. For example: 2, [[1,0]] There are a total of 2 courses to take. To take course 1 you should have finished course 0. So the correct course order is [0,1] 4, [[1,0],[2,0],[3,1],[3,2]] There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0. So one correct course order is [0,1,2,3]. Another correct ordering is[0,2,1,3]. Note: The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented. You may assume that there are no duplicate edges in the input prerequisites.
思路:与I类似,在BFS过程中就记录访问入度为0的节点的顺序。
310. Minimum Height Trees
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
For a undirected graph with tree characteristics, we can choose any node as the root. The result graph is then a rooted tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs). Given such a graph, write a function to find all the MHTs and return a list of their root labels. Format The graph contains n nodes which are labeled from 0 to n - 1. You will be given the number n and a list of undirected edges (each edge is a pair of labels). You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges. Example 1: Given n = 4, edges = [[1, 0], [1, 2], [1, 3]] 0 | 1 / \ 2 3 return [1] Example 2: Given n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]] 0 1 2 \ | / 3 | 4 | 5 return [3, 4] Note: (1) According to the definition of tree on Wikipedia: “a tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any connected graph without simple cycles is a tree.” (2) The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf.
思路:从度为1的叶节点(用一个degree数组记录每个节点的度数)开始,BFS遍历,不需要visited数组标记,当节点的度为0时continue掉即可,最后一次加入到队列中的叶节点即为所求。注意这道题的corner case:只有一个节点,此时结果集是[0]而不是[],切记!时间复杂度O(n),空间复杂度O(n),n为节点个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<Integer> findMinHeightTrees(int n, int[][] edges) { 3 List<Integer> result = new ArrayList<>(); 4 if (n < 1 || edges.length != n - 1) { 5 return result; 6 } 7 if (n == 1) { 8 result.add(0); 9 return result; 10 } 11 Map<Integer, Set<Integer>> graph = initializeGraph(n, edges); 12 int[] degree = new int[n]; 13 for (int i = 0; i < edges.length; i++) { 14 degree[edges[i][0]]++; 15 degree[edges[i][1]]++; 16 } 17 Queue<Integer> queue = new LinkedList<>(); 18 for (int i = 0; i < n; i++) { 19 if (degree[i] == 0) { 20 return result; 21 } 22 if (degree[i] == 1) { 23 queue.offer(i); 24 } 25 } 26 while (!queue.isEmpty()) { 27 result = new ArrayList<>(); 28 int size = queue.size(); 29 for (int i = 0; i < size; i++) { 30 int num = queue.poll(); 31 result.add(num); 32 degree[num]--; 33 for (int neighbor : graph.get(num)) { 34 if (degree[neighbor] == 0) { 35 continue; 36 } 37 degree[neighbor]--; 38 if (degree[neighbor] == 1) { 39 queue.add(neighbor); 40 } 41 } 42 } 43 } 44 return result; 45 } 46 public Map<Integer, Set<Integer>> initializeGraph(int n, int[][] edges) { 47 Map<Integer, Set<Integer>> graph = new HashMap<>(); 48 for (int i = 0; i < n; i++) { 49 graph.put(i, new HashSet<Integer>()); 50 } 51 for (int i = 0; i < edges.length; i++) { 52 graph.get(edges[i][0]).add(edges[i][1]); 53 graph.get(edges[i][1]).add(edges[i][0]); 54 } 55 return graph; 56 } 57 }
133. Clone Graph
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors. OJ's undirected graph serialization: Nodes are labeled uniquely. We use # as a separator for each node, and , as a separator for node label and each neighbor of the node. As an example, consider the serialized graph {0,1,2#1,2#2,2}. The graph has a total of three nodes, and therefore contains three parts as separated by #. First node is labeled as 0. Connect node 0 to both nodes 1 and 2. Second node is labeled as 1. Connect node 1 to node 2. Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle. Visually, the graph looks like the following: 1 / \ / \ 0 --- 2 / \ \_/
思路:两步。1. 复制节点 2. 复制边,这个过程一定要注意当前节点的邻居节点是新的邻居节点而不是老的邻居节点,否则邻居节点就是老的邻居节点的引用,这里很容易犯错误。 预先要BFS得出老节点,这样才能操作1和2。时间复杂度O(n),空间复杂度O(n),n为节点个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for undirected graph. 3 * class UndirectedGraphNode { 4 * int label; 5 * List<UndirectedGraphNode> neighbors; 6 * UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); } 7 * }; 8 */ 9 public class Solution { 10 public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) { 11 if (node == null) { 12 return null; 13 } 14 Map<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap<>(); 15 Set<UndirectedGraphNode> nodes = getNodes(node); 16 // copy node 17 for (UndirectedGraphNode n : nodes) { 18 map.put(n, new UndirectedGraphNode(n.label)); 19 } 20 // copy neighbor 21 for (UndirectedGraphNode n : nodes) { 22 UndirectedGraphNode newNode = map.get(n); 23 for (UndirectedGraphNode neighbor : n.neighbors) { 24 UndirectedGraphNode newNeighbor = map.get(neighbor); 25 newNode.neighbors.add(newNeighbor); 26 } 27 } 28 return map.get(node); 29 } 30 public Set<UndirectedGraphNode> getNodes(UndirectedGraphNode node) { 31 Set<UndirectedGraphNode> set = new HashSet<>(); 32 Queue<UndirectedGraphNode> queue = new LinkedList<>(); 33 queue.offer(node); 34 set.add(node); 35 while (!queue.isEmpty()) { 36 UndirectedGraphNode cur = queue.poll(); 37 for (UndirectedGraphNode neighbor : cur.neighbors) { 38 if (set.contains(neighbor)) { 39 continue; 40 } 41 queue.offer(neighbor); 42 set.add(neighbor); 43 } 44 } 45 return set; 46 } 47 }
130. Surrounded Regions
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a 2D board containing 'X' and 'O' (the letter O), capture all regions surrounded by 'X'. A region is captured by flipping all 'O's into 'X's in that surrounded region. For example, X X X X X O O X X X O X X O X X After running your function, the board should be: X X X X X X X X X X X X X O X X
思路I:BFS。一次AC,时间复杂度O(nm),空间复杂度O(nm)。从二维矩阵四周的'O'开始BFS遍历,将遍历到的'O'设置为'Y'。最后遍历一遍二维矩阵,将'O'设置成'X',表示这些'O'是被捕捉到的;将'Y'设置回'O',表示这些'O'没有捕捉到,恢复。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int x; 3 int y; 4 public Node(int x, int y) { 5 this.x = x; 6 this.y = y; 7 } 8 } 9 public class Solution { 10 public void solve(char[][] board) { 11 if (board == null || board.length == 0 || board[0].length == 0) { 12 return; 13 } 14 int n = board.length; 15 int m = board[0].length; 16 Queue<Node> queue = new LinkedList<>(); 17 for (int i = 0; i < n; i++) { 18 if (board[i][0] == 'O') { 19 queue.offer(new Node(i, 0)); 20 board[i][0] = 'Y'; 21 } 22 if (board[i][m - 1] == 'O') { 23 queue.offer(new Node(i, m - 1)); 24 board[i][m - 1] = 'Y'; 25 } 26 } 27 for (int i = 1; i < m - 1; i++) { 28 if (board[0][i] == 'O') { 29 queue.offer(new Node(0, i)); 30 board[0][i] = 'Y'; 31 } 32 if (board[n - 1][i] == 'O') { 33 queue.offer(new Node(n - 1, i)); 34 board[n - 1][i] = 'Y'; 35 } 36 } 37 int[] dx = {1, 0, -1, 0}; 38 int[] dy = {0, 1, 0, -1}; 39 while (!queue.isEmpty()) { 40 Node cur = queue.poll(); 41 for (int i = 0; i < 4; i++) { 42 int next_x = cur.x + dx[i]; 43 int next_y = cur.y + dy[i]; 44 if (next_x >= 0 && next_x < n && next_y >= 0 && next_y < m && board[next_x][next_y] == 'O') { 45 queue.offer(new Node(next_x, next_y)); 46 board[next_x][next_y] = 'Y'; 47 } 48 } 49 } 50 for (int i = 0; i < n; i++) { 51 for (int j = 0; j < m; j++) { 52 if (board[i][j] == 'O') { 53 board[i][j] = 'X'; 54 } else if (board[i][j] == 'Y') { 55 board[i][j] = 'O'; 56 } 57 } 58 } 59 } 60 }
思路II:并查集。并查集 建立一个n * m的dummy node,遍历二维数组,边上的为字符O的直接合并到dummy node, 在里面的字符为O的根据情况合并,如果该字符与相邻字符O的parent一样就不合并,如果不一样分两种情况,如果该字符的parent为dummy node就将相邻字符O合并到该字符的parent,否则将该字符的parent合并到相邻字符O的parent。最后再遍历一次二维数组,字符为O并且该字符的parent不是dummy node就将该字符置为字符X。也就是说,不能被包围的字符O的parent都是dummy node,能被包围的字符O的parent都不是dummy node。时间复杂度O(nm),空间复杂度O(nm)。
127. Word Ladder
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to endWord, such that: Only one letter can be changed at a time. Each transformed word must exist in the word list. Note that beginWord is not a transformed word. For example, Given: beginWord = "hit" endWord = "cog" wordList = ["hot","dot","dog","lot","log","cog"] As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog", return its length 5. Note: Return 0 if there is no such transformation sequence. All words have the same length. All words contain only lowercase alphabetic characters. You may assume no duplicates in the word list. You may assume beginWord and endWord are non-empty and are not the same. UPDATE (2017/1/20): The wordList parameter had been changed to a list of strings (instead of a set of strings). Please reload the code definition to get the latest changes.
思路:bfs,注意获取下一个单词的方法的写法,是遍历单词的每一个位置,然后对每一个位置用'a' - 'z'替换(当然不能等于该位置上的字符),然后组装成新的串,再看这个串是否在单词集合中。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 public int ladderLength(String beginWord, String endWord, List<String> wordList) { 3 if (beginWord.equals(endWord)) { 4 return 1; 5 } 6 Set<String> set = new HashSet<>(); 7 for (String s : wordList) { 8 set.add(s); 9 } 10 Queue<String> queue = new LinkedList<>(); 11 Set<String> visited = new HashSet<>(); 12 queue.offer(beginWord); 13 visited.add(beginWord); 14 int length = 0; 15 while (!queue.isEmpty()) { 16 length++; 17 int size = queue.size(); 18 for (int i = 0; i < size; i++) { 19 String word = queue.poll(); 20 if (word.equals(endWord)) { 21 return length; 22 } 23 List<String> nextWords = getNextWords(set, word); 24 for (String nextWord : nextWords) { 25 if (visited.contains(nextWord)) { 26 continue; 27 } 28 queue.offer(nextWord); 29 visited.add(nextWord); 30 } 31 } 32 } 33 return 0; 34 } 35 private List<String> getNextWords(Set<String> set, String word) { 36 List<String> result = new ArrayList<>(); 37 for (int i = 0; i < word.length(); i++) { 38 for (char c = 'a'; c <= 'z'; c++) { 39 if (c == word.charAt(i)) { 40 continue; 41 } 42 char[] chars = word.toCharArray(); 43 chars[i] = c; 44 String nextWord = new String(chars); 45 if (set.contains(nextWord)) { 46 result.add(nextWord); 47 } 48 } 49 } 50 return result; 51 } 52 }
126. Word Ladder II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that: Only one letter can be changed at a time Each transformed word must exist in the word list. Note that beginWord is not a transformed word. For example, Given: beginWord = "hit" endWord = "cog" wordList = ["hot","dot","dog","lot","log","cog"] Return [ ["hit","hot","dot","dog","cog"], ["hit","hot","lot","log","cog"] ] Note: Return an empty list if there is no such transformation sequence. All words have the same length. All words contain only lowercase alphabetic characters. You may assume no duplicates in the word list. You may assume beginWord and endWord are non-empty and are not the same. UPDATE (2017/1/20): The wordList parameter had been changed to a list of strings (instead of a set of strings). Please reload the code definition to get the latest changes.
思路:BFS+DFS。首先BFS求出当前节点离起始节点的距离,然后从起始节点开始DFS求出起点到终点的所有最短路径上的单词序列。注意:血的教训!!!题目给定的单词集合不管是List还是Set,统统转化成Set,因为后面求当前节点单词在的下一个出现在单词集合中的单词时,会出现判断新单词是否在单词集合中,也就是会调用contains方法!!!而List的contains方法不是O(1)时间复杂度,所以要用Set作为单词集合!!!
无优化:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) { 3 List<List<String>> result = new ArrayList<>(); 4 if (beginWord.equals(endWord)) { 5 List<String> tempList = new ArrayList<>(); 6 tempList.add(beginWord); 7 result.add(tempList); 8 return result; 9 } 10 Set<String> dict = new HashSet<>(wordList); 11 dict.add(beginWord); 12 Map<String, Integer> distance = new HashMap<>(); 13 bfs(dict, beginWord, distance); 14 dfs(dict, beginWord, endWord, distance, new ArrayList<String>(), result); 15 return result; 16 } 17 public void bfs(Set<String> dict, 18 String beginWord, 19 Map<String, Integer> distance) { 20 21 Queue<String> queue = new LinkedList<>(); 22 queue.offer(beginWord); 23 distance.put(beginWord, 0); 24 while (!queue.isEmpty()) { 25 int size = queue.size(); 26 for (int i = 0; i < size; i++) { 27 String word = queue.poll(); 28 List<String> nextWords = getNextWords(word, dict); 29 for (String nextWord : nextWords) { 30 if (!distance.containsKey(nextWord)) { 31 queue.offer(nextWord); 32 distance.put(nextWord, distance.get(word) + 1); 33 } 34 } 35 } 36 } 37 } 38 public void dfs(Set<String> dict, 39 String word, 40 String endWord, 41 Map<String, Integer> distance, 42 List<String> path, 43 List<List<String>> result) { 44 path.add(word); 45 if (word.equals(endWord)) { 46 result.add(new ArrayList<String>(path)); 47 } else { 48 List<String> nextWords = getNextWords(word, dict); 49 for (String nextWord : nextWords) { 50 if (distance.get(nextWord) == distance.get(word) + 1) { 51 dfs(dict, nextWord, endWord, distance, path, result); 52 } 53 } 54 } 55 path.remove(path.size() - 1); 56 } 57 public List<String> getNextWords(String word, Set<String> dict) { 58 List<String> nextWords = new ArrayList<>(); 59 char[] wordArray = word.toCharArray(); 60 for (int i = 0; i < wordArray.length; i++) { 61 for (char c = 'a'; c <= 'z'; c++) { 62 if (wordArray[i] != c) { 63 char temp = wordArray[i]; 64 wordArray[i] = c; 65 String nextWord = new String(wordArray); 66 if (dict.contains(nextWord)) { 67 nextWords.add(nextWord); 68 } 69 wordArray[i] = temp; 70 } 71 } 72 } 73 return nextWords; 74 } 75 }
优化I:bfs过程将每个单词的下一个单词集合记录下来,在dfs中使用的时候就不用重复求。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) { 3 List<List<String>> result = new ArrayList<>(); 4 if (beginWord.equals(endWord)) { 5 List<String> tempList = new ArrayList<>(); 6 tempList.add(beginWord); 7 result.add(tempList); 8 return result; 9 } 10 Set<String> dict = new HashSet<>(wordList); 11 dict.add(beginWord); 12 Map<String, Integer> distance = new HashMap<>(); 13 Map<String, List<String>> nodeNeighbors = new HashMap<>(); 14 bfs(dict, beginWord, nodeNeighbors, distance); 15 dfs(dict, beginWord, endWord, nodeNeighbors, distance, new ArrayList<String>(), result); 16 return result; 17 } 18 public void bfs(Set<String> dict, 19 String beginWord, 20 Map<String, List<String>> nodeNeighbors, 21 Map<String, Integer> distance) { 22 23 for (String word : dict) { 24 nodeNeighbors.put(word, new ArrayList<String>()); 25 } 26 27 Queue<String> queue = new LinkedList<>(); 28 queue.offer(beginWord); 29 distance.put(beginWord, 0); 30 while (!queue.isEmpty()) { 31 int size = queue.size(); 32 for (int i = 0; i < size; i++) { 33 String word = queue.poll(); 34 List<String> nextWords = getNextWords(word, dict); 35 for (String nextWord : nextWords) { 36 nodeNeighbors.get(word).add(nextWord); 37 if (!distance.containsKey(nextWord)) { 38 queue.offer(nextWord); 39 distance.put(nextWord, distance.get(word) + 1); 40 } 41 } 42 } 43 } 44 } 45 public void dfs(Set<String> dict, 46 String word, 47 String endWord, 48 Map<String, List<String>> nodeNeighbors, 49 Map<String, Integer> distance, 50 List<String> path, 51 List<List<String>> result) { 52 path.add(word); 53 if (word.equals(endWord)) { 54 result.add(new ArrayList<String>(path)); 55 } else { 56 for (String nextWord : nodeNeighbors.get(word)) { 57 if (distance.get(nextWord) == distance.get(word) + 1) { 58 dfs(dict, nextWord, endWord, nodeNeighbors, distance, path, result); 59 } 60 } 61 } 62 path.remove(path.size() - 1); 63 } 64 public List<String> getNextWords(String word, Set<String> dict) { 65 List<String> nextWords = new ArrayList<>(); 66 char[] wordArray = word.toCharArray(); 67 for (int i = 0; i < wordArray.length; i++) { 68 for (char c = 'a'; c <= 'z'; c++) { 69 if (wordArray[i] != c) { 70 char temp = wordArray[i]; 71 wordArray[i] = c; 72 String nextWord = new String(wordArray); 73 if (dict.contains(nextWord)) { 74 nextWords.add(nextWord); 75 } 76 wordArray[i] = temp; 77 } 78 } 79 } 80 return nextWords; 81 } 82 }
优化II:在I的基础上,bfs过程设置一个foundEnd标记,代表是否遍历到了末尾单词。bfs过程只需遍历到末尾单词即可,因为我们只关心从起点到终点的路径。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) { 3 List<List<String>> result = new ArrayList<>(); 4 if (beginWord.equals(endWord)) { 5 List<String> tempList = new ArrayList<>(); 6 tempList.add(beginWord); 7 result.add(tempList); 8 return result; 9 } 10 Set<String> dict = new HashSet<>(wordList); 11 dict.add(beginWord); 12 Map<String, Integer> distance = new HashMap<>(); 13 Map<String, List<String>> nodeNeighbors = new HashMap<>(); 14 bfs(dict, beginWord, endWord, nodeNeighbors, distance); 15 dfs(dict, beginWord, endWord, nodeNeighbors, distance, new ArrayList<String>(), result); 16 return result; 17 } 18 public void bfs(Set<String> dict, 19 String beginWord, 20 String endWord, 21 Map<String, List<String>> nodeNeighbors, 22 Map<String, Integer> distance) { 23 24 for (String word : dict) { 25 nodeNeighbors.put(word, new ArrayList<String>()); 26 } 27 28 Queue<String> queue = new LinkedList<>(); 29 queue.offer(beginWord); 30 distance.put(beginWord, 0); 31 while (!queue.isEmpty()) { 32 boolean foundEnd = false; 33 int size = queue.size(); 34 for (int i = 0; i < size; i++) { 35 String word = queue.poll(); 36 List<String> nextWords = getNextWords(word, dict); 37 for (String nextWord : nextWords) { 38 nodeNeighbors.get(word).add(nextWord); 39 if (!distance.containsKey(nextWord)) { 40 if (nextWord.equals(endWord)) { 41 foundEnd = true; 42 } else { 43 queue.offer(nextWord); 44 } 45 distance.put(nextWord, distance.get(word) + 1); 46 } 47 } 48 } 49 if (foundEnd) { 50 break; 51 } 52 } 53 } 54 public void dfs(Set<String> dict, 55 String word, 56 String endWord, 57 Map<String, List<String>> nodeNeighbors, 58 Map<String, Integer> distance, 59 List<String> path, 60 List<List<String>> result) { 61 path.add(word); 62 if (word.equals(endWord)) { 63 result.add(new ArrayList<String>(path)); 64 } else { 65 for (String nextWord : nodeNeighbors.get(word)) { 66 if (distance.get(nextWord) == distance.get(word) + 1) { 67 dfs(dict, nextWord, endWord, nodeNeighbors, distance, path, result); 68 } 69 } 70 } 71 path.remove(path.size() - 1); 72 } 73 public List<String> getNextWords(String word, Set<String> dict) { 74 List<String> nextWords = new ArrayList<>(); 75 char[] wordArray = word.toCharArray(); 76 for (int i = 0; i < wordArray.length; i++) { 77 for (char c = 'a'; c <= 'z'; c++) { 78 if (wordArray[i] != c) { 79 char temp = wordArray[i]; 80 wordArray[i] = c; 81 String nextWord = new String(wordArray); 82 if (dict.contains(nextWord)) { 83 nextWords.add(nextWord); 84 } 85 wordArray[i] = temp; 86 } 87 } 88 } 89 return nextWords; 90 } 91 }
339. Nested List Weight Sum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a nested list of integers, return the sum of all integers in the list weighted by their depth. Each element is either an integer, or a list -- whose elements may also be integers or other lists. Example 1: Given the list [[1,1],2,[1,1]], return 10. (four 1's at depth 2, one 2 at depth 1) Example 2: Given the list [1,[4,[6]]], return 27. (one 1 at depth 1, one 4 at depth 2, and one 6 at depth 3; 1 + 4*2 + 6*3 = 27)
思路:DFS。递归过程中分两种情况:1. 是Integer,那么就叠加进结果中。2. 是List,那么就递归,深度+1.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * // This is the interface that allows for creating nested lists. 3 * // You should not implement it, or speculate about its implementation 4 * public interface NestedInteger { 5 * 6 * // @return true if this NestedInteger holds a single integer, rather than a nested list. 7 * public boolean isInteger(); 8 * 9 * // @return the single integer that this NestedInteger holds, if it holds a single integer 10 * // Return null if this NestedInteger holds a nested list 11 * public Integer getInteger(); 12 * 13 * // @return the nested list that this NestedInteger holds, if it holds a nested list 14 * // Return null if this NestedInteger holds a single integer 15 * public List<NestedInteger> getList(); 16 * } 17 */ 18 public class Solution { 19 private int sum = 0; 20 public int depthSum(List<NestedInteger> nestedList) { 21 dfs(nestedList, 1); 22 return sum; 23 } 24 public void dfs(List<NestedInteger> nestedList, int depth) { 25 for (NestedInteger nest : nestedList) { 26 if (nest.isInteger()) { 27 sum += depth * nest.getInteger(); 28 } else { 29 dfs(nest.getList(), depth + 1); 30 } 31 } 32 } 33 }
366. Find Leaves of Binary Tree
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a binary tree, collect a tree's nodes as if you were doing this: Collect and remove all leaves, repeat until the tree is empty. Example: Given binary tree 1 / \ 2 3 / \ 4 5 Returns [4, 5, 3], [2], [1]. Explanation: 1. Removing the leaves [4, 5, 3] would result in this tree: 1 / 2 2. Now removing the leaf [2] would result in this tree: 1 3. Now removing the leaf [1] would result in the empty tree: [] Returns [4, 5, 3], [2], [1].
思路I:DFS,模拟操作即可。每一次的DFS存储当前tree的叶节点,然后将叶节点的父亲节点与该叶节点的指针关系移除,这个过程需要记录当前节点的父亲节点以及父亲节点与当前节点的左右儿子关系,用boolean left来表示,为true表示的是左儿子,否则是右儿子。基于这个过程,需要预处理:建立一个dummy代表根节点root的父亲节点,并且明确左右儿子关系,比如dummy.left = root。注意!!!:循环结束的条件是dummy.left != null 而不是root != null,因为root始终不为null,要根据指向关系来判断。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public List<List<Integer>> findLeaves(TreeNode root) { 12 List<List<Integer>> result = new ArrayList<>(); 13 if (root == null) { 14 return result; 15 } 16 TreeNode dummy = new TreeNode(0); 17 dummy.left = root; 18 while (dummy.left != null) { 19 List<Integer> leaves = new ArrayList<>(); 20 dfs(root, dummy, leaves, true); 21 result.add(leaves); 22 } 23 return result; 24 } 25 public void dfs(TreeNode cur, TreeNode father, List<Integer> leaves, boolean left) { 26 if (cur.left == null && cur.right == null) { 27 if (left) { 28 father.left = null; 29 } else { 30 father.right = null; 31 } 32 leaves.add(cur.val); 33 return; 34 } 35 if (cur.left != null) { 36 dfs(cur.left, cur, leaves, true); 37 } 38 if (cur.right != null) { 39 dfs(cur.right, cur, leaves, false); 40 } 41 } 42 }
思路II:BFS + 拓扑排序。BFS分层,先入出度为0的节点,对于当前节点,只要不是根节点root,就将该节点的父节点的出度减1,若出度为0,就入列。基于这个过程,需要两个Map,一个存储节点与该节点的度数的映射,另一个节点存储该节点的父亲节点。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public List<List<Integer>> findLeaves(TreeNode root) { 12 List<List<Integer>> result = new ArrayList<>(); 13 if (root == null) { 14 return result; 15 } 16 Map<TreeNode, Integer> outDegree = new HashMap<>(); 17 Map<TreeNode, TreeNode> father = new HashMap<>(); 18 load(root, outDegree, father); 19 Queue<TreeNode> queue = new LinkedList<>(); 20 for (TreeNode node : outDegree.keySet()) { 21 if (outDegree.get(node) == 0) { 22 queue.offer(node); 23 } 24 } 25 while (!queue.isEmpty()) { 26 List<Integer> leaves = new ArrayList<>(); 27 int size = queue.size(); 28 for (int i = 0; i < size; i++) { 29 TreeNode cur = queue.poll(); 30 leaves.add(cur.val); 31 if (cur != root) { 32 TreeNode pre = father.get(cur); 33 outDegree.put(pre, outDegree.get(pre) - 1); 34 if (outDegree.get(pre) == 0) { 35 queue.offer(pre); 36 } 37 } 38 } 39 result.add(leaves); 40 } 41 return result; 42 } 43 public void load(TreeNode root, Map<TreeNode, Integer> outDegree, Map<TreeNode, TreeNode> father) { 44 if (root == null) { 45 return; 46 } 47 if (root.left == null && root.right == null) { 48 outDegree.put(root, 0); 49 return; 50 } 51 int degree = 0; 52 if (root.left != null) { 53 degree++; 54 father.put(root.left, root); 55 } 56 if (root.right != null) { 57 degree++; 58 father.put(root.right, root); 59 } 60 outDegree.put(root, degree); 61 load(root.left, outDegree, father); 62 load(root.right, outDegree, father); 63 } 64 }
617. Merge Two Binary Trees
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two binary trees and imagine that when you put one of them to cover the other, some nodes of the two trees are overlapped while the others are not. You need to merge them into a new binary tree. The merge rule is that if two nodes overlap, then sum node values up as the new value of the merged node. Otherwise, the NOT null node will be used as the node of new tree. Example 1: Input: Tree 1 Tree 2 1 2 / \ / \ 3 2 1 3 / \ \ 5 4 7 Output: Merged tree: 3 / \ 4 5 / \ \ 5 4 7 Note: The merging process must start from the root nodes of both trees.
思路:DFS,只要有一边为null,返回另一边,不需要再往下。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public TreeNode mergeTrees(TreeNode t1, TreeNode t2) { 12 TreeNode root = null; 13 if (t1 != null && t2 != null) { 14 root = new TreeNode(t1.val + t2.val); 15 root.left = mergeTrees(t1.left, t2.left); 16 root.right = mergeTrees(t1.right, t2.right); 17 } else if (t1 != null) { 18 return t1; 19 } else { 20 return t2; 21 } 22 return root; 23 } 24 }
538. Convert BST to Greater Tree
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original BST is changed to the original key plus sum of all keys greater than the original key in BST. Example: Input: The root of a Binary Search Tree like this: 5 / \ 2 13 Output: The root of a Greater Tree like this: 18 / \ 20 13
思路:逆向中序遍历,即按节点值从大到小遍历节点,递归过程中记录遍历过的所有节点的和。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 private int sum = 0; 12 public TreeNode convertBST(TreeNode root) { 13 dfs(root); 14 return root; 15 } 16 public void dfs(TreeNode root) { 17 if (root == null) { 18 return; 19 } 20 dfs(root.right); 21 root.val += sum; 22 sum = root.val; 23 dfs(root.left); 24 } 25 }
HashTable专题
1. Two Sum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of integers, return indices of the two numbers such that they add up to a specific target. You may assume that each input would have exactly one solution, and you may not use the same element twice. Example: Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
思路I:建议节点类,存储数组元素的索引和索引对应的值。然后按照双指针的做法(要排序)写就可以了。时间复杂度O(nlgn),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int index; 3 int value; 4 public Node(int index, int value) { 5 this.index = index; 6 this.value = value; 7 } 8 } 9 public class Solution { 10 public int[] twoSum(int[] nums, int target) { 11 if (nums == null || nums.length <= 1) { 12 return new int[0]; 13 } 14 Node[] nodes = new Node[nums.length]; 15 for (int i = 0; i < nums.length; i++) { 16 nodes[i] = new Node(i, nums[i]); 17 } 18 Arrays.sort(nodes, new Comparator<Node>() { 19 public int compare(Node a, Node b) { 20 return a.value - b.value; 21 } 22 }); 23 int start = 0; 24 int end = nums.length - 1; 25 while (start < end) { 26 int sum = nodes[start].value + nodes[end].value; 27 if (sum == target) { 28 int index1 = nodes[start].index; 29 int index2 = nodes[end].index; 30 if (index1 <= index2) { 31 return new int[]{index1, index2}; 32 } 33 return new int[]{index2, index1}; 34 } else if (sum < target) { 35 start++; 36 } else { 37 end--; 38 } 39 } 40 return new int[0]; 41 } 42 }
思路II:建立一个Map,存储target - nums[i]和i的映射关系。如果Map包含当前元素值,就找到了;如果不包含,就将target - nums[i]和i的映射放入到Map中。时间复杂度O(n),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int[] twoSum(int[] nums, int target) { 3 if (nums == null || nums.length <= 1) { 4 return new int[0]; 5 } 6 Map<Integer, Integer> map = new HashMap<>(); 7 for (int i = 0; i < nums.length; i++) { 8 if (map.containsKey(nums[i])) { 9 return new int[]{map.get(nums[i]), i}; 10 } 11 map.put(target - nums[i], i); 12 } 13 return new int[0]; 14 } 15 }
136. Single Number
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of integers, every element appears twice except for one. Find that single one. Note: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
思路:利用异或^运算的性质:0 ^ a = a;a ^ a = 0。时间复杂度O(n),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int singleNumber(int[] nums) { 3 if (nums == null || nums.length == 0) { 4 return 0; 5 } 6 int result = 0; 7 for (int i = 0; i < nums.length; i++) { 8 result = result ^ nums[i]; 9 } 10 return result; 11 } 12 }
3. Longest Substring Without Repeating Characters
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a string, find the length of the longest substring without repeating characters. Examples: Given "abcabcbb", the answer is "abc", which the length is 3. Given "bbbbb", the answer is "b", with the length of 1. Given "pwwkew", the answer is "wke", with the length of 3. Note that the answer must be a substring, "pwke" is a subsequence and not a substring.
思路:Map存储非重复字符和其索引的映射,双指针i,j分别指向非重复子串的结束位置和开始位置,遍历i,如果当前字符不在Map中,更新Map,求子串长度来更新最大子串长度;如果当前字符在Map中,更新j的位置( max(j, map.get(c) + 1) ),更新Map,求子串长度来更新最大子串长度。时间复杂度O(n),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean isHappy(int n) { 3 Set<Integer> set = new HashSet<>(); 4 while (n != 1) { 5 if (set.contains(n)) { 6 return false; 7 } 8 set.add(n); 9 n = getNextHappyNumber(n); 10 } 11 return true; 12 } 13 public int getNextHappyNumber(int n) { 14 int sum = 0; 15 while (n != 0) { 16 sum += (n % 10) * (n % 10); 17 n /= 10; 18 } 19 return sum; 20 } 21 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int lengthOfLongestSubstring(String s) { 3 if (s == null || s.length() == 0) { 4 return 0; 5 } 6 Map<Character, Integer> map = new HashMap<>(); 7 int max = 0; 8 for (int i = 0, j = 0; i < s.length(); i++) { 9 if (map.containsKey(s.charAt(i))) { 10 j = Math.max(j, map.get(s.charAt(i)) + 1); 11 } 12 map.put(s.charAt(i), i); 13 max = Math.max(max, i - j + 1); 14 } 15 return max; 16 } 17 }
217. Contains Duplicate
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of integers, find if the array contains any duplicates. Your function should return true if any value appears at least twice in the array, and it should return false if every element is distinct.
思路:用Set判断即可。时间复杂度O(n),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean containsDuplicate(int[] nums) { 3 if (nums == null || nums.length <= 1) { 4 return false; 5 } 6 Set<Integer> set = new HashSet<>(); 7 for (int i = 0; i < nums.length; i++) { 8 if (set.contains(nums[i])) { 9 return true; 10 } 11 set.add(nums[i]); 12 } 13 return false; 14 } 15 }
202. Happy Number
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Write an algorithm to determine if a number is "happy". A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers. Example: 19 is a happy number 12 + 92 = 82 82 + 22 = 68 62 + 82 = 100 12 + 02 + 02 = 1
思路I:Set存储不重复的快乐数,一旦当前快乐数等于1,返回true。否则,如果当前快乐数载Set中出现,则死循环,返回false;如果没出现就加入到Set中。然后根据当前快乐数求的下一个快乐数再进行下一轮判断。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean isHappy(int n) { 3 Set<Integer> set = new HashSet<>(); 4 while (n != 1) { 5 if (set.contains(n)) { 6 return false; 7 } 8 set.add(n); 9 n = getNextHappyNumber(n); 10 } 11 return true; 12 } 13 public int getNextHappyNumber(int n) { 14 int sum = 0; 15 while (n != 0) { 16 sum += (n % 10) * (n % 10); 17 n /= 10; 18 } 19 return sum; 20 } 21 }
思路II:实际上就是判断链表有无环的问题,当有环的时候看环值是否为1。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean isHappy(int n) { 3 int slow = n; 4 int fast = getNextHappyNumber(n); 5 while (slow != fast) { 6 slow = getNextHappyNumber(slow); 7 fast = getNextHappyNumber(fast); 8 fast = getNextHappyNumber(fast); 9 } 10 return slow == 1; 11 } 12 public int getNextHappyNumber(int n) { 13 int sum = 0; 14 while (n != 0) { 15 sum += (n % 10) * (n % 10); 16 n /= 10; 17 } 18 return sum; 19 } 20 }
149. Max Points on a Line
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.
思路:对于每一个固定的点,计算最多有多少个节点与该点在一条直线上。然后对每一个点都执行该操作,就可以求出在一条直线上的最多的点个数。注意点如下:1. 当前节点只需要和该节点没有检测过在一条直线上的点相比,不需要与检测过的点相比。 2. 用Map<Integer, Map<Integer>>存储斜率x, y, 和斜率为(x, y)的点的个数(不包含基准点本身),只有这样斜率的比较才精确,求斜率(x, y)的过程中要注意化到最简,涉及到求最大公约数然后x和y均除以最大公约数,注意最大公约数的求解代码,辗转相除法。用double判断斜率是否相等不精确。3. 重复的基准点单独另外计数。时间复杂度O(n^2),空间复杂度O(n^2)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a point. 3 * class Point { 4 * int x; 5 * int y; 6 * Point() { x = 0; y = 0; } 7 * Point(int a, int b) { x = a; y = b; } 8 * } 9 */ 10 public class Solution { 11 public int maxPoints(Point[] points) { 12 if (points == null || points.length == 0) { 13 return 0; 14 } 15 if (points.length <= 2) { 16 return points.length; 17 } 18 int result = 0; 19 Map<Integer, Map<Integer, Integer>> map = new HashMap<>(); 20 for (int i = 0; i < points.length; i++) { 21 map.clear(); 22 int local_max = 0; 23 int overlaps = 0; 24 for (int j = i + 1; j < points.length; j++) { 25 int x = points[j].x - points[i].x; 26 int y = points[j].y - points[i].y; 27 if (x == 0 && y == 0) { 28 overlaps++; 29 continue; 30 } 31 int gcd = getGcd(x, y); 32 x /= gcd; 33 y /= gcd; 34 if (map.containsKey(x)) { 35 if (map.get(x).containsKey(y)) { 36 map.get(x).put(y, map.get(x).get(y) + 1); 37 } else { 38 map.get(x).put(y, 1); 39 } 40 } else { 41 Map<Integer, Integer> tempMap = new HashMap<>(); 42 tempMap.put(y, 1); 43 map.put(x, tempMap); 44 } 45 local_max = Math.max(local_max, map.get(x).get(y)); 46 } 47 result = Math.max(result, local_max + overlaps + 1); 48 } 49 return result; 50 } 51 public int getGcd(int x, int y) { 52 if (y == 0) { 53 return x; 54 } 55 return getGcd(y, x % y); 56 } 57 }
242. Valid Anagram
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two strings s and t, write a function to determine if t is an anagram of s. For example, s = "anagram", t = "nagaram", return true. s = "rat", t = "car", return false. Note: You may assume the string contains only lowercase alphabets. Follow up: What if the inputs contain unicode characters? How would you adapt your solution to such case?
思路:用数组!不要用Map!我也不知道为啥用Map过不了,Map内部结构问题吧。时间复杂度O(len),空间复杂度O(1)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean isAnagram(String s, String t) { 3 if (s == null && t == null) { 4 return true; 5 } 6 if (s == null || t == null) { 7 return false; 8 } 9 if (s.length() != t.length()) { 10 return false; 11 } 12 int[] count = new int[26]; 13 for (int i = 0; i < s.length(); i++) { 14 count[s.charAt(i) - 'a']++; 15 count[t.charAt(i) - 'a']--; 16 } 17 for (int i = 0; i < 26; i++) { 18 if (count[i] != 0) { 19 return false; 20 } 21 } 22 return true; 23 } 24 }
49. Group Anagrams
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of strings, group anagrams together. For example, given: ["eat", "tea", "tan", "ate", "nat", "bat"], Return: [ ["ate", "eat","tea"], ["nat","tan"], ["bat"] ] Note: All inputs will be in lower-case.
思路:比较巧妙,用一个Map存储基准String和该串对应的所有anagram的集合List<String>,这个基准串就是将当前串排按字母顺序排序,然后将当前串加入到该串的基准串所对应的集合中。时间复杂度O(n*len*lglen),len为字符串平均长度。空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<List<String>> groupAnagrams(String[] strs) { 3 List<List<String>> result = new ArrayList<>(); 4 if (strs == null || strs.length == 0) { 5 return result; 6 } 7 Map<String, List<String>> map = new HashMap<>(); 8 for (int i = 0; i < strs.length; i++) { 9 char[] chars = strs[i].toCharArray(); 10 Arrays.sort(chars); 11 String key = String.valueOf(chars); 12 if (!map.containsKey(key)) { 13 map.put(key, new ArrayList<String>()); 14 } 15 map.get(key).add(strs[i]); 16 } 17 for (String key : map.keySet()) { 18 result.add(map.get(key)); 19 } 20 return result; 21 } 22 }
85. Maximal Rectangle
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area. For example, given the following matrix: 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 Return 6.
思路:二维最大矩阵问题用单调栈。时间复杂度O(nm),空间复杂度O(nm)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maximalRectangle(char[][] matrix) { 3 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 4 return 0; 5 } 6 int n = matrix.length; 7 int m = matrix[0].length; 8 int[][] height = new int[n][m]; 9 for (int i = 0; i < m; i++) { 10 height[0][i] = matrix[0][i] - '0'; 11 } 12 for (int i = 1; i < n; i++) { 13 for (int j = 0; j < m; j++) { 14 if (matrix[i][j] == '0') { 15 height[i][j] = 0; 16 } else { 17 height[i][j] = height[i - 1][j] + 1; 18 } 19 } 20 } 21 int max = 0; 22 for (int i = 0; i < n; i++) { 23 Stack<Integer> stack = new Stack<>(); 24 for (int j = 0; j <= m; j++) { 25 int cur = j == m ? 0 : height[i][j]; 26 while (!stack.isEmpty() && cur <= height[i][stack.peek()]) { 27 int h = height[i][stack.pop()]; 28 int w = stack.isEmpty() ? j : j - stack.peek() - 1; 29 max = Math.max(max, h * w); 30 } 31 stack.push(j); 32 } 33 } 34 return max; 35 } 36 }
DP专题
55. Jump Game
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of non-negative integers, you are initially positioned at the first index of the array. Each element in the array represents your maximum jump length at that position. Determine if you are able to reach the last index. For example: A = [2,3,1,1,4], return true. A = [3,2,1,0,4], return false.
思路I:DP,最后一个testcase会超时,但要掌握。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean canJump(int[] nums) { 3 if (nums == null || nums.length == 0) { 4 return false; 5 } 6 boolean[] f = new boolean[nums.length]; 7 f[0] = true; 8 for (int i = 1; i < nums.length; i++) { 9 for (int j = 0; j < i; j++) { 10 if (f[j] && j + nums[j] >= i) { 11 f[i] = true; 12 break; 13 } 14 } 15 } 16 return f[nums.length - 1]; 17 } 18 }
思路II:贪心。设置一个farthest代表最远能走多远,当当前位置i能到达(i <= farthest)并且i+nums[i] > farthest时就更新farthest。最后判断farthest >= nums.length - 1即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean canJump(int[] nums) { 3 if (nums == null || nums.length == 0) { 4 return false; 5 } 6 int farthest = 0; 7 for (int i = 0; i < nums.length; i++) { 8 if (i <= farthest && i + nums[i] > farthest) { 9 farthest = i + nums[i]; 10 } 11 } 12 return farthest >= nums.length - 1; 13 } 14 }
322. Coin Change
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1. Example 1: coins = [1, 2, 5], amount = 11 return 3 (11 = 5 + 5 + 1) Example 2: coins = [2], amount = 3 return -1. Note: You may assume that you have an infinite number of each kind of coin.
思路:DP。按照DP的四个状态分析就好。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int coinChange(int[] coins, int amount) { 3 if (amount < 0 || coins == null || coins.length == 0) { 4 return -1; 5 } 6 int[] f = new int[amount + 1]; 7 f[0] = 0; 8 for (int i = 1; i <= amount; i++) { 9 f[i] = Integer.MAX_VALUE; 10 for (int j = 0; j < coins.length; j++) { 11 if (i - coins[j] >= 0 && f[i - coins[j]] != Integer.MAX_VALUE && f[i - coins[j]] + 1 < f[i]) { 12 f[i] = f[i - coins[j]] + 1; 13 } 14 } 15 } 16 if (f[amount] == Integer.MAX_VALUE) { 17 return -1; 18 } 19 return f[amount]; 20 } 21 }
62. Unique Paths
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below). How many possible unique paths are there?
思路:DP。滚动数组优化空间复杂度。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int uniquePaths(int m, int n) { 3 if (m <= 0 || n <= 0) { 4 return 0; 5 } 6 int[][] dp = new int[2][n]; 7 for (int i = 0; i < m; i++) { 8 for (int j = 0; j < n; j++) { 9 if (i == 0 || j == 0) { 10 dp[i % 2][j] = 1; 11 } else { 12 dp[i % 2][j] = dp[(i - 1) % 2][j] + dp[i % 2][j - 1]; 13 } 14 } 15 } 16 return dp[(m - 1) % 2][n - 1]; 17 } 18 }
138. Copy List with Random Pointer
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null. Return a deep copy of the list.
思路:类似clone graph,先copy nodes,再copy pointers。时间复杂度O(n),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list with a random pointer. 3 * class RandomListNode { 4 * int label; 5 * RandomListNode next, random; 6 * RandomListNode(int x) { this.label = x; } 7 * }; 8 */ 9 public class Solution { 10 public RandomListNode copyRandomList(RandomListNode head) { 11 if (head == null) { 12 return null; 13 } 14 Map<RandomListNode, RandomListNode> map = new HashMap<>(); 15 RandomListNode cur = head; 16 // copy node 17 while (cur != null) { 18 map.put(cur, new RandomListNode(cur.label)); 19 cur = cur.next; 20 } 21 cur = head; 22 // copy pointers 23 while (cur != null) { 24 if (cur.next != null) { 25 map.get(cur).next = map.get(cur.next); 26 } 27 if (cur.random != null) { 28 map.get(cur).random = map.get(cur.random); 29 } 30 cur = cur.next; 31 } 32 return map.get(head); 33 } 34 }
思路:第一遍扫的时候巧妙运用next指针, 开始数组是1->2->3。 然后扫描过程中 先建立copy节点 1->1`->2->2`->3->3`, 然后第二遍copy的时候去建立边的copy, 拆分节点, 一边扫描一边拆成两个链。第一个链表变回 1->2->3 , 然后第二变成 1`->2`->3。时间复杂度O(n),空间复杂度O(1)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list with a random pointer. 3 * class RandomListNode { 4 * int label; 5 * RandomListNode next, random; 6 * RandomListNode(int x) { this.label = x; } 7 * }; 8 */ 9 public class Solution { 10 public RandomListNode copyRandomList(RandomListNode head) { 11 if (head == null) { 12 return null; 13 } 14 copyNext(head); 15 copyRandom(head); 16 return split(head); 17 } 18 public void copyNext(RandomListNode head) { 19 while (head != null) { 20 RandomListNode newNode = new RandomListNode(head.label); 21 newNode.next = head.next; 22 head.next = newNode; 23 head = head.next.next; 24 } 25 } 26 public void copyRandom(RandomListNode head) { 27 while (head != null) { 28 if (head.random != null) { 29 head.next.random = head.random.next; 30 } 31 head = head.next.next; 32 } 33 } 34 public RandomListNode split(RandomListNode head) { 35 RandomListNode newHead = head.next; 36 while (head != null) { 37 RandomListNode cur = head.next; 38 head.next = head.next.next; 39 if (cur.next != null) { 40 cur.next = cur.next.next; 41 } 42 head = head.next; 43 } 44 return newHead; 45 } 46 }
217. Contains Duplicate
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of integers, find if the array contains any duplicates. Your function should return true if any value appears at least twice in the array, and it should return false if every element is distinct.
思路:用Set判断重复元素。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean containsDuplicate(int[] nums) { 3 if (nums == null || nums.length <= 1) { 4 return false; 5 } 6 Set<Integer> set = new HashSet<>(); 7 for (int i = 0; i < nums.length; i++) { 8 if (!set.contains(nums[i])) { 9 set.add(nums[i]); 10 } else { 11 return true; 12 } 13 } 14 return false; 15 } 16 }
219. Contains Duplicate II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of integers and an integer k, find out whether there are two distinct indices i and j in the array such that nums[i] = nums[j] and the absolute difference between i and j is at most k.
思路:用Map存储元素 -> 索引的映射,来判断重复元素的索引的绝对距离是否小于等于k,如果是返回true,否则更新该重复元素的索引。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean containsNearbyDuplicate(int[] nums, int k) { 3 if (nums == null || nums.length == 0 || k <= 0) { 4 return false; 5 } 6 Map<Integer, Integer> map = new HashMap<>(); 7 for (int i = 0; i < nums.length; i++) { 8 if (!map.containsKey(nums[i])) { 9 map.put(nums[i], i); 10 } else { 11 if (i - map.get(nums[i]) <= k) { 12 return true; 13 } else { 14 map.put(nums[i], i); 15 } 16 } 17 } 18 return false; 19 } 20 }
36. Valid Sudoku
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. The Sudoku board could be partially filled, where empty cells are filled with the character '.'. A partially filled sudoku which is valid. Note: A valid Sudoku board (partially filled) is not necessarily solvable. Only the filled cells need to be validated.
思路:先检测行和列是否有重复元素,再检测每个3*3块是否有重复元素即可。时间复杂度O(n^2),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 private int n; 3 private int m; 4 public boolean isValidSudoku(char[][] board) { 5 if (board == null || board.length == 0 || board[0].length == 0) { 6 return false; 7 } 8 n = board.length; 9 m = board[0].length; 10 if (n != m || n % 3 != 0) { 11 return false; 12 } 13 for (int i = 0; i < n; i++) { 14 if (detectLine(board, i, true)) { 15 return false; 16 } 17 } 18 for (int i = 0; i < m; i++) { 19 if (detectLine(board, i, false)) { 20 return false; 21 } 22 } 23 for (int i = 0; i < n; i += 3) { 24 for (int j = 0; j < m; j += 3) { 25 if (detectBlock(board, i, j)) { 26 return false; 27 } 28 } 29 } 30 return true; 31 } 32 public boolean detectLine(char[][] board, int x, boolean flag) { 33 Set<Character> set = new HashSet<>(); 34 if (flag) { 35 for (int i = 0; i < m; i++) { 36 if (board[x][i] == '.') { 37 continue; 38 } 39 if (set.contains(board[x][i])) { 40 return true; 41 } else { 42 set.add(board[x][i]); 43 } 44 } 45 } else { 46 for (int i = 0; i < n; i++) { 47 if (board[i][x] == '.') { 48 continue; 49 } 50 if (set.contains(board[i][x])) { 51 return true; 52 } else { 53 set.add(board[i][x]); 54 } 55 } 56 } 57 return false; 58 } 59 public boolean detectBlock(char[][] board, int x, int y) { 60 Set<Character> set = new HashSet<>(); 61 for (int i = x; i < x + 3; i++) { 62 for (int j = y; j < y + 3; j++) { 63 if (board[i][j] == '.') { 64 continue; 65 } 66 if (set.contains(board[i][j])) { 67 return true; 68 } else { 69 set.add(board[i][j]); 70 } 71 } 72 } 73 return false; 74 } 75 }
187. Repeated DNA Sequences
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: "ACGAATTCCG". When studying DNA, it is sometimes useful to identify repeated sequences within the DNA. Write a function to find all the 10-letter-long sequences (substrings) that occur more than once in a DNA molecule. For example, Given s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT", Return: ["AAAAACCCCC", "CCCCCAAAAA"].
思路:形成所有长度为10的子串,加入到Set中,如果Set中包含子串,说明重复,就把该串加入到临时Set中,否则,加入该子串到Set。采用临时子串是为了处理如"AAAAAAAAAAAA"(长度为12),当出现大于2个的重复子串出现时,如果用List就会将该子串重复加入到List中,而且用List的contains方法去重的话不是O(1)的时间复杂度,故用临时Set来存储,最后将临时Set中的元素复制到List中返回即可。时间复杂度O(len),空间复杂度O(len)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<String> findRepeatedDnaSequences(String s) { 3 List<String> result = new ArrayList<>(); 4 if (s == null || s.length() < 10) { 5 return result; 6 } 7 Set<String> set = new HashSet<>(); 8 Set<String> temp = new HashSet<>(); 9 for (int i = 0; i <= s.length() - 10; i++) { 10 String str = s.substring(i, i + 10); 11 if (!set.contains(str)) { 12 set.add(str); 13 } else { 14 temp.add(str); 15 } 16 } 17 return new ArrayList<>(temp); 18 } 19 }
349. Intersection of Two Arrays
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two arrays, write a function to compute their intersection. Example: Given nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2]. Note: Each element in the result must be unique. The result can be in any order.
题意:求两个数组的不重复的公共元素
思路:对第一个数组,将其所有元素加入到第一个Set中去重,对于第二个数组的每个元素,如果第一个Set中含有这个元素,说明该元素是公共元素,将该公共元素加入到第二个Set中(公共元素去重),否则继续下一个元素的检测。时间复杂度O(len),空间复杂度O(len)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int[] intersection(int[] nums1, int[] nums2) { 3 if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) { 4 return new int[0]; 5 } 6 Set<Integer> set = new HashSet<>(); 7 Set<Integer> result = new HashSet<>(); 8 for (int num : nums1) { 9 set.add(num); 10 } 11 for (int num : nums2) { 12 if (set.contains(num)) { 13 result.add(num); 14 } 15 } 16 int[] array = new int[result.size()]; 17 int index = 0; 18 for (int num : result) { 19 array[index++] = num; 20 } 21 return array; 22 } 23 }
336. Palindrome Pairs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a list of unique words, find all pairs of distinct indices (i, j) in the given list, so that the concatenation of the two words, i.e. words[i] + words[j] is a palindrome. Example 1: Given words = ["bat", "tab", "cat"] Return [[0, 1], [1, 0]] The palindromes are ["battab", "tabbat"] Example 2: Given words = ["abcd", "dcba", "lls", "s", "sssll"] Return [[0, 1], [1, 0], [3, 2], [2, 4]] The palindromes are ["dcbaabcd", "abcddcba", "slls", "llssssll"]
题意:求字符串数组中任意两个单词能组成回文串的所有索引对数。
思路:对于每个单词,从左到右遍历一遍,对于任意一个位置,如果它的左边是回文串且右边的逆序在字典中出现,那么就存在这么一种组合,同理,如果它的右边是回文串且左边的逆序在字典中出现,那么也存在这么一种组合。
考虑空串如 ["a", ""]这种情况,j <= words[i].length,即j要取到words[i].length。为了去重,使用Set<List<Integer>>来存储结果,最后再复制给List<List<Integer>>。时间复杂度O(n*len*len),len为字符串平均长度。
["a", ""] 对于单词"a" "" "a" add (0, 1) "a" "" add (1, 0)
对于单词"" "" "" 没有add
["abcd", "dcba"] 对于单词"abcd"的其中一种情况 "" "abcd" add (1, 0) "abcd" "" add (0, 1)
对于单词"dcba"的其中一种情况 "" "dcba" add (0, 1) "dcba" "" add (1, 0)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<List<Integer>> palindromePairs(String[] words) { 3 List<List<Integer>> result = new ArrayList<>(); 4 if (words == null || words.length <= 1) { 5 return result; 6 } 7 Set<List<Integer>> set = new HashSet<>(); 8 Map<String, Integer> map = new HashMap<>(); 9 for (int i = 0; i < words.length; i++) { 10 map.put(words[i], i); 11 } 12 for (int i = 0; i < words.length; i++) { 13 for (int j = 0; j <= words[i].length(); j++) { 14 String str1 = words[i].substring(0, j); 15 String str2 = words[i].substring(j); 16 if (isPalindrome(str1)) { 17 String str2_reverse = new StringBuilder(str2).reverse().toString(); 18 if (map.containsKey(str2_reverse) && map.get(str2_reverse) != i) { 19 List<Integer> tempList = new ArrayList<>(); 20 tempList.add(map.get(str2_reverse)); 21 tempList.add(i); 22 set.add(tempList); 23 } 24 } 25 if (isPalindrome(str2)) { 26 String str1_reverse = new StringBuilder(str1).reverse().toString(); 27 if (map.containsKey(str1_reverse) && map.get(str1_reverse) != i) { 28 List<Integer> tempList = new ArrayList<>(); 29 tempList.add(i); 30 tempList.add(map.get(str1_reverse)); 31 set.add(tempList); 32 } 33 } 34 } 35 } 36 return new ArrayList<List<Integer>>(set); 37 } 38 public boolean isPalindrome(String s) { 39 int start = 0; 40 int end = s.length() - 1; 41 while (start < end) { 42 if (s.charAt(start) != s.charAt(end)) { 43 return false; 44 } 45 start++; 46 end--; 47 } 48 return true; 49 } 50 }
347. Top K Frequent Elements
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a non-empty array of integers, return the k most frequent elements. For example, Given [1,1,1,2,2,3] and k = 2, return [1,2]. Note: You may assume k is always valid, 1 ≤ k ≤ number of unique elements. Your algorithm's time complexity must be better than O(n log n), where n is the array's size.
题意:求前k高频的整数。
思路:这道题要求时间复杂度比O(nlgn)更快,就不能用堆来做。首先Map存储整数 -> 该整数出现的次数 的映射关系,然后用一个List数组将该Map映射翻转过来,索引为整数的次数,索引对应的List装该整数,这样就把出现次数相同的整数放到一个索引所对应的List中。最后从数组的最后往前遍历,将元素加入到结果集中,注意:只要结果集的size大于等于k就退出,返回结果集。时间复杂度O(n),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<Integer> topKFrequent(int[] nums, int k) { 3 List<Integer> result = new ArrayList<>(); 4 if (nums == null || nums.length == 0 || k <= 0) { 5 return result; 6 } 7 Map<Integer, Integer> frequency = new HashMap<>(); 8 List<Integer>[] bucket = new List[nums.length + 1]; 9 for (int num : nums) { 10 if (!frequency.containsKey(num)) { 11 frequency.put(num, 1); 12 } else { 13 frequency.put(num, frequency.get(num) + 1); 14 } 15 } 16 for (int num : frequency.keySet()) { 17 if (bucket[frequency.get(num)] == null) { 18 bucket[frequency.get(num)] = new ArrayList<Integer>(); 19 } 20 bucket[frequency.get(num)].add(num); 21 } 22 for (int i = nums.length; i >= 0 && result.size() < k; i--) { 23 if (bucket[i] != null) { 24 result.addAll(bucket[i]); 25 } 26 } 27 return result; 28 } 29 }
204. Count Primes
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Description:
Count the number of prime numbers less than a non-negative number, n.
题意:求比n小的所有素数个数。
思路:素数:大于1的只能被1和自身整除的数。建一个长度为n的boolean数组,初始化均为false表示均为素数,然后从2开始统计素数个数,对于每一个素数i,将其整数倍(从i倍开始)的元素置为true,表示非素数,如果当前遍历的是非素数就跳过。注意i * j < n这句当n很大时i * j会溢出,所以比较的时候要提升至long型来比较,写成:(long) i * j < n。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int countPrimes(int n) { 3 if (n <= 1) { 4 return 0; 5 } 6 boolean[] notPrime = new boolean[n]; 7 int count = 0; 8 for (int i = 2; i < n; i++) { 9 if (notPrime[i] == false) { 10 count++; 11 for (int j = i; (long) i * j < n; j++) { 12 notPrime[i * j] = true; 13 } 14 } 15 } 16 return count; 17 } 18 }
205. Isomorphic Strings
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two strings s and t, determine if they are isomorphic. Two strings are isomorphic if the characters in s can be replaced to get t. All occurrences of a character must be replaced with another character while preserving the order of characters. No two characters may map to the same character but a character may map to itself. For example, Given "egg", "add", return true. Given "foo", "bar", return false. Given "paper", "title", return true. Note: You may assume both s and t have the same length.
题意:判断两个长度相同的字符串的模式是否相同
思路:两个字符串的每个字符的映射是一一映射。不存在多对一和一对多的情况。这道题的解法可以学到:Map中不仅有containsKey方法,还有containsValue方法。时间复杂度O(len)。空间复杂度O(len)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean isIsomorphic(String s, String t) { 3 if (s == null && t == null) { 4 return true; 5 } 6 if (s == null || t == null) { 7 return false; 8 } 9 if (s.length() != t.length()) { 10 return false; 11 } 12 Map<Character, Character> map = new HashMap<>(); 13 for (int i = 0; i < s.length(); i++) { 14 char a = s.charAt(i); 15 char b = t.charAt(i); 16 if (!map.containsKey(a)) { 17 if (!map.containsValue(b)) { 18 map.put(a, b); 19 } else { 20 return false; 21 } 22 } else { 23 if (map.get(a).equals(b)) { 24 continue; 25 } else { 26 return false; 27 } 28 } 29 } 30 return true; 31 } 32 33 }
299. Bulls and Cows
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
You are playing the following Bulls and Cows game with your friend: You write down a number and ask your friend to guess what the number is. Each time your friend makes a guess, you provide a hint that indicates how many digits in said guess match your secret number exactly in both digit and position (called "bulls") and how many digits match the secret number but locate in the wrong position (called "cows"). Your friend will use successive guesses and hints to eventually derive the secret number. For example: Secret number: "1807" Friend's guess: "7810" Hint: 1 bull and 3 cows. (The bull is 8, the cows are 0, 1 and 7.) Write a function to return a hint according to the secret number and friend's guess, use A to indicate the bulls and B to indicate the cows. In the above example, your function should return "1A3B". Please note that both secret number and friend's guess may contain duplicate digits, for example: Secret number: "1123" Friend's guess: "0111" In this case, the 1st 1 in friend's guess is a bull, the 2nd or 3rd 1 is a cow, and your function should return "1A1B". You may assume that the secret number and your friend's guess only contain digits, and their lengths are always equal.
思路:对于bulls直接看两个串的相同位置上的字符是否相同来统计,对于cows则用一个数组来统计字符出现的次数,细节见代码。时间复杂度O(n),空间复杂度O(1)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public String getHint(String secret, String guess) { 3 if (secret == null || secret.length() == 0 || guess == null || guess.length() == 0) { 4 return ""; 5 } 6 int[] count = new int[10]; 7 int bulls = 0; 8 int cows = 0; 9 for (int i = 0; i < secret.length(); i++) { 10 char s = secret.charAt(i); 11 char g = guess.charAt(i); 12 if (s == g) { 13 bulls++; 14 } else { 15 if (count[s - '0'] < 0) { 16 cows++; 17 } 18 if (count[g - '0'] > 0) { 19 cows++; 20 } 21 count[s - '0']++; 22 count[g - '0']--; 23 } 24 } 25 return bulls + "A" + cows + "B"; 26 } 27 }
76. Minimum Window Substring
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n). For example, S = "ADOBECODEBANC" T = "ABC" Minimum window is "BANC". Note: If there is no such window in S that covers all characters in T, return the empty string "". If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.
题意:求s中的包含有t中所有字符的最小长度子串。
思路:建立两个hash,sourceHash和targetHash(长度均为256)分别存储s中子串的每个字符出现的次数和t中每个字符出现的次数。然后对于s中每一个开始的位置i,从该位置开始搜寻其后的一段子串使该子串满足包含t中所有字符,对所有的i都进行这样的操作,取满足条件的长度最小的子串即位结果。注意在遍历的过程中,位置j负责从i开始搜寻满足条件的子串,j指针一直往前走,不会回头,当前[i, j)子串满足条件后,下一次遍历,i往后挪一位,j从上次遍历的最后的位置开始继续向前走,因此时间复杂度为O(n),空间复杂度O(1)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public String minWindow(String s, String t) { 3 if (s == null || s.length() == 0 || t == null || t.length() == 0) { 4 return ""; 5 } 6 int[] targetHash = init(t); 7 int[] sourceHash = new int[256]; 8 int min_length = Integer.MAX_VALUE; 9 String min_str = ""; 10 for (int i = 0, j = 0; i < s.length(); i++) { 11 while (j < s.length() && !valid(sourceHash, targetHash)) { 12 sourceHash[s.charAt(j)]++; 13 j++; 14 } 15 if (valid(sourceHash, targetHash)) { 16 if (j - i < min_length) { 17 min_length = j - i; 18 min_str = s.substring(i, j); 19 } 20 } 21 sourceHash[s.charAt(i)]--; 22 } 23 return min_str; 24 } 25 public int[] init(String t) { 26 int[] targetHash = new int[256]; 27 for (int i = 0; i < t.length(); i++) { 28 targetHash[t.charAt(i)]++; 29 } 30 return targetHash; 31 } 32 public boolean valid(int[] sourceHash, int[] targetHash) { 33 for (int i = 0; i < 256; i++) { 34 if (sourceHash[i] < targetHash[i]) { 35 return false; 36 } 37 } 38 return true; 39 } 40 }
166. Fraction to Recurring Decimal
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two integers representing the numerator and denominator of a fraction, return the fraction in string format. If the fractional part is repeating, enclose the repeating part in parentheses. For example, Given numerator = 1, denominator = 2, return "0.5". Given numerator = 2, denominator = 1, return "2". Given numerator = 2, denominator = 3, return "0.(6)".
思路:有很多细节需要处理。1. 符号 2. 去除符号之后需要取绝对值,这个过程可能溢出(Integer.MIN_VALUE取绝对值时会溢出),所以取绝对值的时候先强制转换为long型再取绝对值,然后分子和分母都变成long型来运算。整数部分很好处理,直接加入到StringBuilder中。小数部分的处理如下:用一个HashMap存储余数 --> 要开始做处罚运算的地方,也就是sb.length()。这样当当前运算出的余数在HashMap中的时候,说明是无限循环小数部分,就在HashMap中该余数对应的value值的地方插入"(",再在StringBuilder末尾加入")"。如果没出现在HashMap中就将键值对存入HashMap中,继续循环,直到为0退出。最后返回sb.toString()即为答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public String fractionToDecimal(int numerator, int denominator) { 3 if (denominator == 0) { 4 return ""; 5 } 6 if (numerator == 0) { 7 return "0"; 8 } 9 StringBuilder sb = new StringBuilder(); 10 if (numerator > 0 && denominator < 0 || numerator < 0 && denominator > 0) { 11 sb.append("-"); 12 } 13 long num = Math.abs((long)numerator); 14 long den = Math.abs((long)denominator); 15 // integer part 16 sb.append(num / den); 17 num %= den; 18 if (num == 0) { 19 return sb.toString(); 20 } 21 // fractional part 22 sb.append("."); 23 Map<Long, Integer> map = new HashMap<>(); 24 map.put(num, sb.length()); 25 while (num != 0) { 26 num *= 10; 27 sb.append(num / den); 28 num %= den; 29 if (map.containsKey(num)) { 30 sb.insert(map.get(num), "("); 31 sb.append(")"); 32 break; 33 } else { 34 map.put(num, sb.length()); 35 } 36 } 37 return sb.toString(); 38 } 39 }
350. Intersection of Two Arrays II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two arrays, write a function to compute their intersection. Example: Given nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2, 2]. Note: Each element in the result should appear as many times as it shows in both arrays. The result can be in any order. Follow up: What if the given array is already sorted? How would you optimize your algorithm? What if nums1's size is small compared to nums2's size? Which algorithm is better? What if elements of nums2 are stored on disk, and the memory is limited such that you cannot load all elements into the memory at once?
题目:求两个数组的公共元素(包含重复元素)
思路:对第一个数组,用HashMap存储元素 --> 该元素出现的次数。对于第二个数组的每一个元素,看是否在HashMap里并且value值不为0,如果是就加入到结果集中,并且将HashMap中相应的value值减1。最后返回结果集。时间复杂度O(n),空间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int[] intersect(int[] nums1, int[] nums2) { 3 if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) { 4 return new int[0]; 5 } 6 Map<Integer, Integer> map = new HashMap<>(); 7 for (int num : nums1) { 8 if (!map.containsKey(num)) { 9 map.put(num, 1); 10 } else { 11 map.put(num, map.get(num) + 1); 12 } 13 } 14 List<Integer> list = new ArrayList<>(); 15 for (int num : nums2) { 16 if (map.containsKey(num) && map.get(num) != 0) { 17 list.add(num); 18 map.put(num, map.get(num) - 1); 19 } 20 } 21 int[] result = new int[list.size()]; 22 int index = 0; 23 for (int i = 0; i < list.size(); i++) { 24 result[index++] = list.get(i); 25 } 26 return result; 27 } 28 }
37. Sudoku Solver
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Write a program to solve a Sudoku puzzle by filling the empty cells. Empty cells are indicated by the character '.'. You may assume that there will be only one unique solution.
题意:求数游戏的解决方案
思路:对于棋盘上每一个空白区域,从1 ~ 9逐一尝试填入,看填上某一数字之后合不合乎规则,如果合乎就将该区域填入该数字,继续递归。如果填入1 ~ 9都不符合,则说明无解,该区域还原为空白区域。时间复杂度O(9^n),n为空白区域的个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public void solveSudoku(char[][] board) { 3 if (board == null || board.length == 0 || board[0].length == 0) { 4 return; 5 } 6 int n = board.length; 7 int m = board[0].length; 8 if (n != 9 || m != 9) { 9 return; 10 } 11 solve(board); 12 } 13 public boolean solve(char[][] board) { 14 for (int i = 0; i < board.length; i++) { 15 for (int j = 0; j < board[0].length; j++) { 16 if (board[i][j] == '.') { 17 for (char c = '1'; c <= '9'; c++) { 18 if (valid(board, i, j, c)) { 19 board[i][j] = c; 20 if (solve(board)) { 21 return true; 22 } else { 23 board[i][j] = '.'; 24 } 25 } 26 } 27 return false; 28 } 29 } 30 } 31 return true; 32 } 33 public boolean valid(char[][] board, int x, int y, char c) { 34 for (int i = 0; i < 9; i++) { 35 // check row 36 if (board[x][i] != '.' && board[x][i] == c) { 37 return false; 38 } 39 // check column 40 if (board[i][y] != '.' && board[i][y] == c) { 41 return false; 42 } 43 // check block 44 if (board[(x / 3) * 3 + i / 3][(y / 3) * 3 + i % 3] != '.' 45 && board[(x / 3) * 3 + i / 3][(y / 3) * 3 + i % 3] == c) { 46 return false; 47 } 48 } 49 return true; 50 } 51 }
463. Island Perimeter
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
You are given a map in form of a two-dimensional integer grid where 1 represents land and 0 represents water. Grid cells are connected horizontally/vertically (not diagonally). The grid is completely surrounded by water, and there is exactly one island (i.e., one or more connected land cells). The island doesn't have "lakes" (water inside that isn't connected to the water around the island). One cell is a square with side length 1. The grid is rectangular, width and height don't exceed 100. Determine the perimeter of the island. Example: [[0,1,0,0], [1,1,1,0], [0,1,0,0], [1,1,0,0]] Answer: 16
题目:求岛屿周长
思路:公式:4 * 岛屿个数 - 2 * 公共部分个数。时间复杂度O(n*m),空间复杂度O(1)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int islandPerimeter(int[][] grid) { 3 if (grid == null || grid.length == 0 || grid[0].length == 0) { 4 return 0; 5 } 6 int n = grid.length; 7 int m = grid[0].length; 8 int islands = 0; 9 int neighbors = 0; 10 for (int i = 0; i < n; i++) { 11 for (int j = 0; j < m; j++) { 12 if (grid[i][j] == 1) { 13 islands++; 14 if (j < m - 1 && grid[i][j + 1] == 1) { 15 neighbors++; 16 } 17 if (i < n - 1 && grid[i + 1][j] == 1) { 18 neighbors++; 19 } 20 } 21 } 22 } 23 return 4 * islands - 2 * neighbors; 24 } 25 }
18. 4Sum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target. Note: The solution set must not contain duplicate quadruplets. For example, given array S = [1, 0, -1, 0, -2, 2], and target = 0. A solution set is: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
题意:给定一个数组,求该数组中的四个数的和等于一个给定值的所有组合情况。
思路:双指针,注意去重。时间复杂度O(n^3)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<List<Integer>> fourSum(int[] nums, int target) { 3 List<List<Integer>> result = new ArrayList<>(); 4 if (nums == null || nums.length < 4) { 5 return result; 6 } 7 Arrays.sort(nums); 8 for (int i = 0; i < nums.length - 3; i++) { 9 if (i > 0 && nums[i] == nums[i - 1]) { 10 continue; 11 } 12 for (int j = i + 1; j < nums.length - 2; j++) { 13 if (j > i + 1 && nums[j] == nums[j - 1]) { 14 continue; 15 } 16 int start = j + 1; 17 int end = nums.length - 1; 18 while (start < end) { 19 int sum = nums[i] + nums[j] + nums[start] + nums[end]; 20 if (sum < target) { 21 start++; 22 } else if (sum == target) { 23 List<Integer> tempList = new ArrayList<>(); 24 tempList.add(nums[i]); 25 tempList.add(nums[j]); 26 tempList.add(nums[start]); 27 tempList.add(nums[end]); 28 result.add(tempList); 29 start++; 30 end--; 31 while (start < end && nums[start] == nums[start - 1]) { 32 start++; 33 } 34 while (start < end && nums[end] == nums[end + 1]) { 35 end--; 36 } 37 } else { 38 end--; 39 } 40 } 41 } 42 } 43 return result; 44 } 45 }
30. Substring with Concatenation of All Words
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters. For example, given: s: "barfoothefoobarman" words: ["foo", "bar"] You should return the indices: [0,9]. (order does not matter).
思路:思路:固定起点i(外层循环),从起点开始搜索(内层循环),如果能搜索到一个窗口恰好满足该窗口内的子串是words中所有单词的结合,就将起点i加入到结果集中;否则终止内层循环,i移到下一个起点。时间复杂度O(nk),n为字符串长度,k为数组长度。空间复杂度O(k)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<Integer> findSubstring(String s, String[] words) { 3 List<Integer> result = new ArrayList<>(); 4 if (s == null || s.length() == 0 || words == null || words.length == 0 5 || s.length() < words.length * words[0].length()) { 6 return result; 7 } 8 Map<String, Integer> map = new HashMap<>(); 9 for (String word : words) { 10 if (!map.containsKey(word)) { 11 map.put(word, 1); 12 } else { 13 map.put(word, map.get(word) + 1); 14 } 15 } 16 int wordLen = words[0].length(); 17 for (int i = 0; i <= s.length() - words.length * wordLen; i++) { 18 Map<String, Integer> temp = new HashMap<>(); 19 int j = 0; 20 for (j = 0; j < words.length; j++) { 21 String word = s.substring(i + j * wordLen, i + (j + 1) * wordLen); 22 if (!map.containsKey(word)) { 23 break; 24 } 25 if (!temp.containsKey(word)) { 26 temp.put(word, 1); 27 } else { 28 temp.put(word, temp.get(word) + 1); 29 } 30 if (temp.get(word) > map.get(word)) { 31 break; 32 } 33 } 34 if (j == words.length) { 35 result.add(i); 36 } 37 } 38 return result; 39 } 40 }
389. Find the Difference
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two strings s and t which consist of only lowercase letters. String t is generated by random shuffling string s and then add one more letter at a random position. Find the letter that was added in t. Example: Input: s = "abcd" t = "abcde" Output: e Explanation: 'e' is the letter that was added.
思路I:HasmMap版本,HashMap存储s中每个字符和对应出现的次数,对于t中的每个字符,出现在HashMap中次数减1,直到该字符不在HashMap中或者在HashMap中出现的次数为0。注意:返回空字符是返回'\0'而不是''。O(n), O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public char findTheDifference(String s, String t) { 3 if (s == null || t == null || s.length() != t.length() - 1) { 4 return '\0'; 5 } 6 Map<Character, Integer> map = new HashMap<>(); 7 for (int i = 0; i < s.length(); i++) { 8 char c = s.charAt(i); 9 if (!map.containsKey(c)) { 10 map.put(c, 1); 11 } else { 12 map.put(c, map.get(c) + 1); 13 } 14 } 15 for (int i = 0; i < t.length(); i++) { 16 char c = t.charAt(i); 17 if (!map.containsKey(c) || map.get(c) == 0) { 18 return c; 19 } 20 map.put(c, map.get(c) - 1); 21 } 22 return '\0'; 23 } 24 }
思路II:异或运算。初始化char c = 0,表示ascii码为0所代表的字符。将s和t的每个字符逐一异或就得到要求的结果。O(n),O(1)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public char findTheDifference(String s, String t) { 3 if (s == null || t == null || s.length() != t.length() - 1) { 4 return '\0'; 5 } 6 char c = 0; 7 for (int i = 0; i < s.length(); i++) { 8 c ^= s.charAt(i); 9 } 10 for (int i = 0; i < t.length(); i++) { 11 c ^= t.charAt(i); 12 } 13 return c; 14 } 15 }
311. Sparse Matrix Multiplication
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two sparse matrices A and B, return the result of AB. You may assume that A's column number is equal to B's row number. Example: A = [ [ 1, 0, 0], [-1, 0, 3] ] B = [ [ 7, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 1 ] ] | 1 0 0 | | 7 0 0 | | 7 0 0 | AB = | -1 0 3 | x | 0 0 0 | = | -7 0 3 | | 0 0 1 |
题意:两个矩阵相乘
思路I:naive,运行时间较高。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int[][] multiply(int[][] A, int[][] B) { 3 if (A == null || A.length == 0 || A[0].length == 0 4 || B == null || B.length == 0 || B[0].length == 0) { 5 return new int[0][0]; 6 } 7 if (A[0].length != B.length) { 8 return new int[0][0]; 9 } 10 int n = A.length; 11 int m = A[0].length; 12 int l = B[0].length; 13 int[][] C = new int[n][l]; 14 for (int i = 0; i < n; i++) { 15 for (int j = 0; j < l; j++) { 16 for (int k = 0; k < m; k++) { 17 if (A[i][k] != 0 && B[k][j] != 0) { 18 C[i][j] += A[i][k] * B[k][j]; 19 } 20 } 21 } 22 } 23 return C; 24 } 25 }
思路II:叠加和,其实就是改变了矩阵相乘的运算顺序。这道题让我们实现稀疏矩阵相乘,稀疏矩阵的特点是矩阵中绝大多数的元素为0,而相乘的结果是还应该是稀疏矩阵,即还是大多数元素为0,那么我们使用传统的矩阵相乘的算法肯定会处理大量的0乘0的无用功,所以我们需要适当的优化算法,使其可以顺利通过OJ,我们知道一个 i x k 的矩阵A乘以一个 k x j 的矩阵B会得到一个 i x j 大小的矩阵C,那么我们来看结果矩阵中的某个元素C[i][j]是怎么来的,起始是A[i][0]*B[0][j] + A[i][1]*B[1][j] + ... + A[i][k]*B[k][j],那么为了不重复计算0乘0,我们首先遍历A数组,要确保A[i][k]不为0,才继续计算,然后我们遍历B矩阵的第k行,如果B[K][J]不为0,我们累加结果矩阵res[i][j] += A[i][k] * B[k][j]; 这样我们就能高效的算出稀疏矩阵的乘法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int[][] multiply(int[][] A, int[][] B) { 3 if (A == null || A.length == 0 || A[0].length == 0 4 || B == null || B.length == 0 || B[0].length == 0) { 5 return new int[0][0]; 6 } 7 if (A[0].length != B.length) { 8 return new int[0][0]; 9 } 10 int n = A.length; 11 int m = A[0].length; 12 int l = B[0].length; 13 int[][] C = new int[n][l]; 14 for (int i = 0; i < n; i++) { 15 for (int k = 0; k < m; k++) { 16 if (A[i][k] != 0) { 17 for (int j = 0; j < l; j++) { 18 if (B[k][j] != 0) { 19 C[i][j] += A[i][k] * B[k][j]; 20 } 21 } 22 } 23 } 24 } 25 return C; 26 } 27 }
274. H-Index
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of citations (each citation is a non-negative integer) of a researcher, write a function to compute the researcher's h-index. According to the definition of h-index on Wikipedia: "A scientist has index h if h of his/her N papers have at least h citations each, and the other N − h papers have no more than h citations each." For example, given citations = [3, 0, 6, 1, 5], which means the researcher has 5 papers in total and each of them had received 3, 0, 6, 1, 5 citations respectively. Since the researcher has 3 papers with at least 3 citations each and the remaining two with no more than 3 citations each, his h-index is 3. Note: If there are several possible values for h, the maximum one is taken as the h-index. Credits: Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases.
思路I:排序,O(nlgn)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int hIndex(int[] citations) { 3 if (citations == null || citations.length == 0) { 4 return 0; 5 } 6 int len = citations.length; 7 Arrays.sort(citations); 8 int i = len - 1; 9 for (; i >= 0; i--) { 10 if (citations[i] < len - i) { 11 break; 12 } 13 } 14 i++; 15 return len - i; 16 } 17 }
思路II:O(n),记录一个数组,索引位引用次数,值为该引用次数的文章数,大于文章总数的引用次数当成文章总数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int hIndex(int[] citations) { 3 if (citations == null || citations.length == 0) { 4 return 0; 5 } 6 int[] count = new int[citations.length + 1]; 7 for (int i = 0; i < citations.length; i++) { 8 if (citations[i] > citations.length) { 9 count[citations.length]++; 10 } else { 11 count[citations[i]]++; 12 } 13 } 14 int sum = 0; 15 for (int i = citations.length; i >= 0; i--) { 16 sum += count[i]; 17 if (sum >= i) { 18 return i; 19 } 20 } 21 return 0; 22 } 23 }
链表专题
141. Linked List Cycle
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a linked list, determine if it has a cycle in it. Follow up: Can you solve it without using extra space?
题意:判断一个链表是否有环
思路:快慢指针,slow=head,fast=head.next开始,slow每次走一步,fast每次走两步。时间复杂度O(n),空间复杂度O(1)。
版本一
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { 7 * val = x; 8 * next = null; 9 * } 10 * } 11 */ 12 public class Solution { 13 public boolean hasCycle(ListNode head) { 14 if (head == null) { 15 return false; 16 } 17 ListNode slow = head; 18 ListNode fast = head.next; 19 while (fast != null && fast.next != null) { 20 if (slow == fast) { 21 return true; 22 } 23 slow = slow.next; 24 fast = fast.next.next; 25 } 26 return false; 27 } 28 }
版本二
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { 7 * val = x; 8 * next = null; 9 * } 10 * } 11 */ 12 public class Solution { 13 public boolean hasCycle(ListNode head) { 14 if (head == null) { 15 return false; 16 } 17 ListNode slow = head; 18 ListNode fast = head.next; 19 while (slow != fast) { 20 if (fast == null || fast.next == null) { 21 return false; 22 } 23 slow = slow.next; 24 fast = fast.next.next; 25 } 26 return true; 27 } 28 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a linked list, return the node where the cycle begins. If there is no cycle, return null. Note: Do not modify the linked list. Follow up: Can you solve it without using extra space?
题意:确定链表中环的入口节点
思路:先找到快慢指针的相遇节点,然后slow回到头结点即slow=head,fast向前移动一位即fast=fast.next,然后slow和fast一起同时向前移动,每次移动一步,知道两个指针再次相遇,该相遇节点即为环的入口节点。
复杂度分析:时间复杂度O(n),空间复杂度O(1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { 7 * val = x; 8 * next = null; 9 * } 10 * } 11 */ 12 public class Solution { 13 public ListNode detectCycle(ListNode head) { 14 if (head == null) { 15 return null; 16 } 17 ListNode slow = head; 18 ListNode fast = head.next; 19 while (slow != fast) { 20 if (fast == null || fast.next == null) { 21 return null; 22 } 23 slow = slow.next; 24 fast = fast.next.next; 25 } 26 slow = head; 27 fast = fast.next; 28 while (slow != fast) { 29 slow = slow.next; 30 fast = fast.next; 31 } 32 return slow; 33 } 34 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Write a program to find the node at which the intersection of two singly linked lists begins. For example, the following two linked lists: A: a1 → a2 ↘ c1 → c2 → c3 ↗ B: b1 → b2 → b3 begin to intersect at node c1. Notes: If the two linked lists have no intersection at all, return null. The linked lists must retain their original structure after the function returns. You may assume there are no cycles anywhere in the entire linked structure. Your code should preferably run in O(n) time and use only O(1) memory.
题意:判读两个链表的公共节点
思路:先把第一个链表的尾节点的next域指向第二个链表的头结点,问题转化为求一个环形链表的入口节点。快慢指针来解决,当找到slow和fast的相遇节点后,还没完!重新修改快慢指针指向:slow=headA,fast=fast.next,然后slow和fast一起每次向前走一步,直到两者相遇,此时相遇的节点就是环形链表的入口节点。注意在返回公共节点之前一定要把第一个链表的尾节点的next指向修改回原来的null,以免改变原链表结构!时间复杂度O(n),空间复杂度O(1)。
版本一
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { 7 * val = x; 8 * next = null; 9 * } 10 * } 11 */ 12 public class Solution { 13 public ListNode getIntersectionNode(ListNode headA, ListNode headB) { 14 if (headA == null || headB == null) { 15 return null; 16 } 17 ListNode tailA = headA; 18 while (tailA.next != null) { 19 tailA = tailA.next; 20 } 21 tailA.next = headB; 22 ListNode slow = headA; 23 ListNode fast = headA.next; 24 while (fast != null && fast.next != null) { 25 if (slow == fast) { 26 slow = headA; 27 fast = fast.next; 28 while (slow != fast) { 29 slow = slow.next; 30 fast = fast.next; 31 } 32 tailA.next = null; 33 return slow; 34 } 35 slow = slow.next; 36 fast = fast.next.next; 37 } 38 tailA.next = null; 39 return null; 40 } 41 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { 7 * val = x; 8 * next = null; 9 * } 10 * } 11 */ 12 public class Solution { 13 public ListNode getIntersectionNode(ListNode headA, ListNode headB) { 14 if (headA == null || headB == null) { 15 return null; 16 } 17 ListNode tailA = headA; 18 while (tailA.next != null) { 19 tailA = tailA.next; 20 } 21 tailA.next = headB; 22 ListNode slow = headA; 23 ListNode fast = headA.next; 24 while (slow != fast) { 25 if (fast == null || fast.next == null) { 26 tailA.next = null; 27 return null; 28 } 29 slow = slow.next; 30 fast = fast.next.next; 31 } 32 slow = headA; 33 fast = fast.next; 34 while (slow != fast) { 35 slow = slow.next; 36 fast = fast.next; 37 } 38 tailA.next = null; 39 return slow; 40 } 41 }
237. Delete Node in a Linked List
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Write a function to delete a node (except the tail) in a singly linked list, given only access to that node.
Supposed the linked list is 1 -> 2 -> 3 -> 4 and you are given the third node with value 3, the linked list should become 1 -> 2 -> 4 after calling your function.
题意:给定要删除的节点,从链表中删除该节点。注意没有给定链表头结点!即无法得到当前节点的前驱节点!
思路:要删除节点的val值赋值成该节点的next节点的val值,然后修改该节点的next指向该节点next的next节点。时间复杂度O(1),空间复杂度O(1)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 class Solution { 10 public void deleteNode(ListNode node) { 11 if (node == null || node.next == null) { 12 return; 13 } 14 node.val = node.next.val; 15 node.next = node.next.next; 16 } 17 }
83. Remove Duplicates from Sorted List
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a sorted linked list, delete all duplicates such that each element appear only once. For example, Given 1->1->2, return 1->2. Given 1->1->2->3->3, return 1->2->3.
题意:从链表中删除重复的元素,保留一个重复的元素。
思路:前后指针prev和cur,初始指向head和head.next,当两个指针所指向的节点值相同时,改变prev的next指向cur的next节点,同时cur指针后移一位;否则,prev和cur都后移一位。
复杂度分析:时间复杂度O(n),空间复杂度O(1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 class Solution { 10 public ListNode deleteDuplicates(ListNode head) { 11 if (head == null || head.next == null) { 12 return head; 13 } 14 ListNode prev = head; 15 ListNode cur = head.next; 16 while (cur != null) { 17 if (prev.val == cur.val) { 18 prev.next = cur.next; 19 cur = cur.next; 20 } else { 21 prev = cur; 22 cur = cur.next; 23 } 24 } 25 return head; 26 } 27 }
82. Remove Duplicates from Sorted List II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list. For example, Given 1->2->3->3->4->4->5, return 1->2->5. Given 1->1->1->2->3, return 2->3.
题意:从链表中删除重复的元素,剔除所有重复的元素。
思路:由于最后返回的头结点不确定,所以建dummy节点,dummy.next = head。前后指针pre和cur,pre始终指向最近一次非重复的节点,cur指向当前待校验的节点。初始pre指向dummy,cur指向head,当cur.next != null && cur.val == cur.next.val时循环,cur往后移动。退出循环时,如果pre的next不是cur,说明cur所指的节点是重复节点,pre.next = cur.next,cur后移一位,注意pre不能后移,因为cur这时所指向的新节点依然可能是重复节点,需要在下一层循环中来判断;如果pre的next是cur,说明cur节点不是重复节点,pre后移一位,cur后移一位继续外层循环,直到cur为null为止。
复杂度分析:时间复杂度O(n),空间复杂度O(1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 class Solution { 10 public ListNode deleteDuplicates(ListNode head) { 11 if (head == null || head.next == null) { 12 return head; 13 } 14 ListNode dummy = new ListNode(0), pre = dummy; 15 dummy.next = head; 16 ListNode cur = head; 17 while (cur != null) { 18 while (cur.next != null && cur.val == cur.next.val) { 19 cur = cur.next; 20 } 21 if (pre.next == cur) { 22 pre = pre.next; 23 } else { 24 pre.next = cur.next; 25 } 26 cur = cur.next; 27 } 28 return dummy.next; 29 } 30 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Remove all elements from a linked list of integers that have value val. Example Given: 1 --> 2 --> 6 --> 3 --> 4 --> 5 --> 6, val = 6 Return: 1 --> 2 --> 3 --> 4 --> 5 Credits: Special thanks to @mithmatt for adding this problem and creating all test cases.
题意:从链表中删除节点值是给定值val得节点
思路:注意到链表头结点可能会发生改变,所以先建立一个dummy节点,其next指向head,然后和83类似。前后指针prev和cur,初始分别指向dummy和head,当cur所指向节点的val值等于给定值时,改变prev的next指向cur的next节点,同时cur指针后移一位;否则,prev和cur都后移一位。
错误点:因为链表头结点可能会发生改变,原来的head节点可能会从链表中剔除,所以最后返回的链表头结点一定是dummy.next而不是head!!!
复杂度分析:时间复杂度O(n),空间复杂度O(1)
206. Reverse Linked List
题目:Reverse a singly linked list.
思路:前后指针prev和cur,初始分别指向null和head,每次翻转先存储cur的next节点after,然后cur的next域指向prev,接着移动指针:prev指向cur,cur指向after。最后cur==null时结束翻转,此时链表头结点为prev,返回prev即可。
复杂度分析:时间复杂度O(n),空间复杂度O(1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 class Solution { 10 public ListNode reverseList(ListNode head) { 11 if (head == null || head.next == null) { 12 return head; 13 } 14 ListNode prev = null; 15 ListNode cur = head; 16 while (cur != null) { 17 ListNode after = cur.next; 18 cur.next = prev; 19 prev = cur; 20 cur = after; 21 } 22 return prev; 23 } 24 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 Reverse a linked list from position m to n. Do it in-place and in one-pass. 2 3 For example: 4 Given 1->2->3->4->5->NULL, m = 2 and n = 4, 5 6 return 1->4->3->2->5->NULL. 7 8 Note: 9 Given m, n satisfy the following condition: 10 1 ≤ m ≤ n ≤ length of list.
题意:反转链表中第m个节点到第n个节点,从1开始。1<=m<=n<=linkedlist.length
思路:由于最后返回的头节点不确定,所以先建立一个dummy节点,dummy.next=head。反转分三个过程:1 反转第m个节点到第n个节点 2 第m个节点的前一个节点的next指向第n个节点 3 第m个节点的next指向第n个节点的后一个节点。
复杂度分析:时间复杂度O(n),空间复杂度O(1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 class Solution { 10 public ListNode reverseBetween(ListNode head, int m, int n) { 11 if (head == null || head.next == null || m >= n || m <= 0) { 12 return head; 13 } 14 ListNode dummy = new ListNode(0); 15 dummy.next = head; 16 ListNode node = dummy; 17 for (int i = 0; i < m - 1; i++) { 18 node = node.next; 19 } 20 ListNode nodeM = node.next; 21 ListNode pre = null, cur = nodeM; 22 for (int i = 0; i < n - m + 1; i++) { 23 ListNode after = cur.next; 24 cur.next = pre; 25 pre = cur; 26 cur = after; 27 } 28 ListNode nodeN = pre; 29 node.next = nodeN; 30 nodeM.next = cur; 31 return dummy.next; 32 } 33 }
234. Palindrome Linked List
题目:Given a singly linked list, determine if it is a palindrome. 判断一个链表是不是回文链表。
思路:受206翻转链表的启发,将原链表的后半部分翻转,然后一头一尾指针逐一校验。翻转后半部分链表的步骤:首先确定中间节点(偶数个节点的话偏头部的那个作为中间节点),用快慢指针来确定,初始slow=head,fast=head.next,循环直到fast==null || fast.next == null(步骤跟确定环形链表入口节点相似),此时slow所指向的节点就是该链表的中间节点。然后跟206步骤一样prev=null,cur=slow开始进行链表翻转。最后从链表头尾逐一校验,循环条件是head != null && tail != null,只要有一个节点为null,说明比较结束,该链表是回文链表。
错误点:从链表头尾逐一校验的循环过程中,当两个节点值不相等直接返回false,不相等记得要移动两个指针到下一个节点!!!再循环比较。
复杂度分析:时间复杂度O(n),空间复杂度O(1)
21. Merge Two Sorted Lists
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists. Example: Input: 1->2->4, 1->3->4 Output: 1->1->2->3->4->4
题意:合并两个有序链表
思路:因为最后返回的链表头结点不确定,所以首先建立虚拟节点dummy,然后新建cur节点指向dummy,然后逐一比较两个链表当前节点值得大小,哪个节点值小cur的next域就指向哪个节点,然后cur前移一位,值小的那个节点指针前移一位,一直比较直到某一节点为null。此时再把剩下的非null的节点(可能在第一个链表也可能在第二个链表上,但是不可能同时存在)连接起来。
复杂度分析:时间复杂度O(n),空间复杂度O(1)
23. Merge k Sorted Lists
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
题意:是上一题21的扩展版本,合并k个有序链表。
思路I:分治思想,转化为合并两个有序链表的问题。
复杂度分析:时间复杂度O(klgn),n为链表的条数,k为链表的平均长度,空间复杂度O(lgn)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 class Solution { 10 public ListNode mergeKLists(ListNode[] lists) { 11 if (lists == null || lists.length == 0) { 12 return null; 13 } 14 return mergeHelper(lists, 0, lists.length - 1); 15 } 16 public ListNode mergeHelper(ListNode[] lists, int start, int end) { 17 if (start == end) { 18 return lists[start]; 19 } 20 int mid = start + (end - start) / 2; 21 ListNode left = mergeHelper(lists, start, mid); 22 ListNode right = mergeHelper(lists, mid + 1, end); 23 return mergeTwoLists(left, right); 24 } 25 public ListNode mergeTwoLists(ListNode left, ListNode right) { 26 ListNode dummy = new ListNode(0), cur = dummy; 27 while (left != null && right != null) { 28 if (left.val <= right.val) { 29 cur.next = left; 30 left = left.next; 31 } else { 32 cur.next = right; 33 right = right.next; 34 } 35 cur = cur.next; 36 } 37 if (left != null) { 38 cur.next = left; 39 } 40 if (right != null) { 41 cur.next = right; 42 } 43 return dummy.next; 44 } 45 }
思路II:最小堆
复杂度分析:因为PriorityQueue的offer和poll操作的复杂度都是O(lgn),n为优先级队列的最大容量。所以时间复杂度为O(nklgn),空间复杂度O(n),n为链表的条数,k为链表的平均长度
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 class Solution { 10 public ListNode mergeKLists(ListNode[] lists) { 11 if (lists == null || lists.length == 0) { 12 return null; 13 } 14 PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.length, new Comparator<ListNode>() { 15 public int compare(ListNode a, ListNode b) { 16 return a.val - b.val; 17 } 18 }); 19 for (int i = 0; i < lists.length; i++) { 20 if (lists[i] != null) { 21 pq.offer(lists[i]); 22 } 23 } 24 ListNode dummy = new ListNode(0), cur = dummy; 25 while (!pq.isEmpty()) { 26 ListNode node = pq.poll(); 27 cur.next = node; 28 cur = cur.next; 29 if (node.next != null) { 30 pq.offer(node.next); 31 } 32 } 33 return dummy.next; 34 35 } 36 }
61. Rotate List
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a list, rotate the list to the right by k places, where k is non-negative.
题意:将链表的末尾的k个节点旋转到头部
思路:首先最后返回的链表头结点不确定,所以建立一个虚拟节点dummy,其next指向head。确定第几个节点后的所有节点要移到首部,这个第几个就是length - k % length,所以开始要计算出链表的长度即节点总个数。确定了该节点后,改变指针指向即可。注意指针指向的改变顺序,要先连接中间的部分,然后再连接首和尾!具体分析见代码里面的注释。
复杂度分析:时间复杂度O(n),空间复杂度O(1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 class Solution { 10 public ListNode rotateRight(ListNode head, int k) { 11 if (head == null || head.next == null || k <= 0) { 12 return head; 13 } 14 ListNode dummy = new ListNode(0); 15 dummy.next = head; 16 int length = 0; 17 ListNode fast = dummy; 18 while (fast.next != null) { 19 fast = fast.next; 20 length++; 21 } 22 int count = length - k % length; 23 ListNode slow = dummy; 24 for (int i = 0; i < count; i++) { 25 slow = slow.next; 26 } 27 // 这里注意一定要先执行fast.next = head,即先把链表中间的部分连接好,再连接首尾,否则会出现错误。比如case:【1,2】,k=2,不先执行 // fast.next = head的话,dummy.next直接就为null了,最后返回的dummy.next就是null,造成错误! 28 fast.next = head; 29 dummy.next = slow.next; 30 slow.next = null; 31 return dummy.next; 32 } 33 }