九章算法强化班
Advanced Data Structure -- Union Find
Number of Islands
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a boolean 2D matrix, 0 is represented as the sea, 1 is represented as the island. If two 1 is adjacent, we consider them in the same island. We only consider up/down/left/right adjacent. Find the number of islands. Have you met this question in a real interview? Yes Example Given graph: [ [1, 1, 0, 0, 0], [0, 1, 0, 0, 1], [0, 0, 0, 1, 1], [0, 0, 0, 0, 0], [0, 0, 0, 0, 1] ] return 3.
思路I:BFS 避免相同位置的元素重复入列,访问过的元素都要标记为已访问。BFS为了得到下一个点的坐标,所以需要建立一个表示位置的坐标类。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Coordinate { 2 public int x; 3 public int y; 4 public Coordinate(int x, int y) { 5 this.x = x; 6 this.y = y; 7 } 8 } 9 public class Solution { 10 /** 11 * @param grid a boolean 2D matrix 12 * @return an integer 13 */ 14 public int numIslands(boolean[][] grid) { 15 // Write your code here 16 if (grid == null || grid.length == 0) { 17 return 0; 18 } 19 int n = grid.length; 20 int m = grid[0].length; 21 int islands = 0; 22 for (int i = 0; i < n; i++) { 23 for (int j = 0; j < m; j++) { 24 if (grid[i][j]) { 25 islands++; 26 mark(grid, i, j); 27 } 28 } 29 } 30 return islands; 31 } 32 private void mark(boolean[][] grid, int x, int y) { 33 int[] directionX = {1, 0, -1, 0}; 34 int[] directionY = {0, -1, 0, 1}; 35 Queue<Coordinate> queue = new LinkedList<>(); 36 queue.offer(new Coordinate(x, y)); 37 grid[x][y] = false; 38 while (!queue.isEmpty()) { 39 Coordinate coordinate = queue.poll(); 40 for (int i = 0; i < 4; i++) { 41 Coordinate adj = new Coordinate( 42 coordinate.x + directionX[i], 43 coordinate.y + directionY[i]); 44 if (!inBound(adj, grid)) { 45 continue; 46 } 47 if (grid[adj.x][adj.y]) { 48 grid[adj.x][adj.y] = false; 49 queue.offer(adj); 50 } 51 } 52 } 53 } 54 private boolean inBound(Coordinate coordinate, boolean[][] grid) { 55 int n = grid.length; 56 int m = grid[0].length; 57 return coordinate.x >= 0 && coordinate.x < n && coordinate.y >= 0 && coordinate.y < m; 58 } 59 }
思路II:并查集 等价于求集合中连通块的个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class UnionFind { 2 private int[] father = null; 3 private int count; 4 public UnionFind(int n, int num) { 5 father = new int[n]; 6 count = num; 7 } 8 public int find(int x) { 9 if (father[x] == 0) { 10 return x; 11 } 12 return find(father[x]); 13 } 14 public void connect(int a, int b) { 15 int root_a = find(a); 16 int root_b = find(b); 17 if (root_a != root_b) { 18 father[root_a] = root_b; 19 count--; 20 } 21 } 22 public int query() { 23 return count; 24 } 25 } 26 public class Solution { 27 /** 28 * @param grid a boolean 2D matrix 29 * @return an integer 30 */ 31 public int numIslands(boolean[][] grid) { 32 // Write your code here 33 if (grid == null || grid.length == 0 || grid[0].length == 0) { 34 return 0; 35 } 36 int n = grid.length; 37 int m = grid[0].length; 38 int count = 0; 39 for (int i = 0; i < n; i++) { 40 for (int j = 0; j < m; j++) { 41 if (grid[i][j]) { 42 count++; 43 } 44 } 45 } 46 UnionFind union_find = new UnionFind(n * m, count); 47 for (int i = 0; i < n; i++) { 48 for (int j = 0; j < m; j++) { 49 if (grid[i][j]) { 50 if (i > 0 && grid[i - 1][j]) { 51 union_find.connect(i * m + j, (i - 1) * m + j); 52 } 53 if (i < n - 1 && grid[i + 1][j]) { 54 union_find.connect(i * m + j, (i + 1) * m + j); 55 } 56 if (j > 0 && grid[i][j - 1]) { 57 union_find.connect(i * m + j, i * m + j - 1); 58 } 59 if (j < m - 1 && grid[i][j + 1]) { 60 union_find.connect(i * m + j, i * m + j + 1); 61 } 62 } 63 } 64 } 65 return union_find.query(); 66 } 67 }
Number of Islands II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a n,m which means the row and column of the 2D matrix and an array of pair A( size k). Originally, the 2D matrix is all 0 which means there is only sea in the matrix. The list pair has k operator and each operator has two integer A[i].x, A[i].y means that you can change the grid matrix[A[i].x][A[i].y] from sea to island. Return how many island are there in the matrix after each operator. Notice 0 is represented as the sea, 1 is represented as the island. If two 1 is adjacent, we consider them in the same island. We only consider up/down/left/right adjacent. Have you met this question in a real interview? Yes Example Given n = 3, m = 3, array of pair A = [(0,0),(0,1),(2,2),(2,1)]. return [1,1,2,2].
思路:并查集,把二维数组转化为一维father数组。
![](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 class UnionFind { 11 private int[] father = null; 12 private int count; 13 public UnionFind(int n, int num) { 14 father = new int[n]; 15 for (int i = 0; i < n; i++) { 16 father[i] = i; 17 } 18 count = num; 19 } 20 public int find(int x) { 21 if (father[x] == x) { 22 return x; 23 } 24 return father[x] = find(father[x]); 25 } 26 public void connect(int a, int b) { 27 int root_a = find(a); 28 int root_b = find(b); 29 if (root_a != root_b) { 30 father[root_a] = root_b; 31 count--; 32 } 33 } 34 public int query() { 35 return count; 36 } 37 public void setCount() { 38 count++; 39 } 40 } 41 public class Solution { 42 /** 43 * @param n an integer 44 * @param m an integer 45 * @param operators an array of point 46 * @return an integer array 47 */ 48 public List<Integer> numIslands2(int n, int m, Point[] operators) { 49 // Write your code here 50 List<Integer> result = new ArrayList<>(); 51 if (n < 1 || m < 1 || operators == null || operators.length == 0) { 52 return result; 53 } 54 int[][] island = new int[n][m]; 55 int[] dx = {1, 0, -1, 0}; 56 int[] dy = {0, 1, 0, -1}; 57 UnionFind union_find = new UnionFind(n * m, 0); 58 for (int i = 0; i < operators.length; i++) { 59 Point point = operators[i]; 60 if (island[point.x][point.y] == 1) { 61 result.add(union_find.query()); 62 continue; 63 } 64 union_find.setCount(); 65 island[point.x][point.y] = 1; 66 for (int j = 0; j < 4; j++) { 67 Point nextPoint = new Point(point.x + dx[j], point.y + dy[j]); 68 if (inBound(nextPoint, n, m) 69 && island[nextPoint.x][nextPoint.y] == 1) { 70 union_find.connect(point.x * m + point.y, nextPoint.x * m + nextPoint.y); 71 } 72 } 73 result.add(union_find.query()); 74 } 75 return result; 76 } 77 public boolean inBound(Point point, int n, int m) { 78 if (point.x < 0 || point.x >= n) { 79 return false; 80 } 81 if (point.y < 0 || point.y >= m) { 82 return false; 83 } 84 return true; 85 } 86 }
LeetCode 547. Friend Circles
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
There are N students in a class. Some of them are friends, while some are not. Their friendship is transitive in nature. For example, if A is a direct friend of B, and B is a direct friend of C, then A is an indirect friend of C. And we defined a friend circle is a group of students who are direct or indirect friends. Given a N*N matrix M representing the friend relationship between students in the class. If M[i][j] = 1, then the ith and jth students are direct friends with each other, otherwise not. And you have to output the total number of friend circles among all the students. Example 1: Input: [[1,1,0], [1,1,0], [0,0,1]] Output: 2 Explanation:The 0th and 1st students are direct friends, so they are in a friend circle. The 2nd student himself is in a friend circle. So return 2. Example 2: Input: [[1,1,0], [1,1,1], [0,1,1]] Output: 1 Explanation:The 0th and 1st students are direct friends, the 1st and 2nd students are direct friends, so the 0th and 2nd students are indirect friends. All of them are in the same friend circle, so return 1. Note: N is in range [1,200]. M[i][i] = 1 for all students. If M[i][j] = 1, then M[j][i] = 1.
思路:并查集的典型应用。题目是求朋友圈的个数,求将这n个人看成n个节点,等价于求连通块的个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class UnionFind { 2 private int[] father; 3 private int count; 4 public UnionFind(int n) { 5 father = new int[n]; 6 for (int i = 0; i < father.length; i++) { 7 father[i] = i; 8 } 9 count = n; 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 getCount() { 26 return count; 27 } 28 } 29 public class Solution { 30 public int findCircleNum(int[][] M) { 31 if (M == null || M.length == 0 || M[0].length == 0) { 32 return 0; 33 } 34 int n = M.length; 35 UnionFind union_find = new UnionFind(n); 36 for (int i = 0; i < n - 1; i++) { 37 for (int j = i + 1; j < n; j++) { 38 if (M[i][j] == 1) { 39 union_find.union(i, j); 40 } 41 } 42 } 43 return union_find.getCount(); 44 } 45 }
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. Notice 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. Have you met this question in a real interview? Yes 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.
思路I:BFS 最后判断连通的节点个数是否为n。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param n an integer 4 * @param edges a list of undirected edges 5 * @return true if it's a valid tree, or false 6 */ 7 public boolean validTree(int n, int[][] edges) { 8 // Write your code here 9 if (n == 0) { 10 return false; 11 } 12 if (edges.length != n - 1) { 13 return false; 14 } 15 Map<Integer, Set<Integer>> graph = 16 initializeGraph(n, edges); 17 Queue<Integer> queue = new LinkedList<>(); 18 Set<Integer> hash = new HashSet<>(); 19 queue.offer(0); 20 hash.add(0); 21 int visited = 0; 22 while (!queue.isEmpty()) { 23 int node = queue.poll(); 24 visited++; 25 for (Integer neighbor : graph.get(node)) { 26 if (hash.contains(neighbor)) { 27 continue; 28 } 29 queue.offer(neighbor); 30 hash.add(neighbor); 31 } 32 } 33 return visited == n; 34 } 35 public Map<Integer, Set<Integer>> initializeGraph(int n, int[][] edges) { 36 Map<Integer, Set<Integer>> graph = new HashMap<>(); 37 for (int i = 0; i < n; i++) { 38 graph.put(i, new HashSet<Integer>()); 39 } 40 for (int i = 0; i < edges.length; i++) { 41 int u = edges[i][0]; 42 int v = edges[i][1]; 43 graph.get(u).add(v); 44 graph.get(v).add(u); 45 } 46 return graph; 47 } 48 }
思路II:并查集 判断n - 1条边是否构成环,即判断是否有两个点的father一样。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class UnionFind { 2 private int[] father = null; 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 /** 25 * @param n an integer 26 * @param edges a list of undirected edges 27 * @return true if it's a valid tree, or false 28 */ 29 public boolean validTree(int n, int[][] edges) { 30 // Write your code here 31 if (n < 1) { 32 return false; 33 } 34 if ((edges == null || edges.length == 0 || edges[0].length == 0) && n != 1) { 35 return false; 36 } 37 if (n - 1 != edges.length) { 38 return false; 39 } 40 UnionFind union_find = new UnionFind(n); 41 for (int i = 0; i < edges.length; i++) { 42 if (union_find.find(edges[i][0]) == union_find.find(edges[i][1])) { 43 return false; 44 } 45 union_find.union(edges[i][0], edges[i][1]); 46 } 47 return true; 48 } 49 }
Surrounded Reigions
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'. A region is captured by flipping all 'O''s into 'X''s in that surrounded region. Have you met this question in a real interview? Yes Example X X X X X O O X X X O X X O X X After capture all regions surrounded by 'X', the board should be: X X X X X X X X X X X X X O X X
思路I:BFS BFS二位矩阵的四周,讲从外向内的字符O都标记为字符V代表已访问过的字符O,然后依次便利整个二位矩阵,字符为O的都是除了四周的里面的O,将它置为字符X,为V的字符还原成原来的字符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 private static final char visited = 'V'; 11 /** 12 * @param board a 2D board containing 'X' and 'O' 13 * @return void 14 */ 15 public void surroundedRegions(char[][] board) { 16 // Write your code here 17 if (board == null || board.length == 0 || board[0].length == 0) { 18 return; 19 } 20 int n = board.length; 21 int m = board[0].length; 22 for (int i = 0; i < n; i++) { 23 bfs(board, i, 0); 24 bfs(board, i, m - 1); 25 } 26 for (int i = 0; i < m; i++) { 27 bfs(board, 0, i); 28 bfs(board, n - 1, i); 29 } 30 for (int i = 0; i < n; i++) { 31 for (int j = 0; j < m; j++) { 32 if (board[i][j] == visited) { 33 board[i][j] = 'O'; 34 } else if (board[i][j] == 'O') { 35 board[i][j] = 'X'; 36 } 37 } 38 } 39 return; 40 } 41 public void bfs(char[][] board, int i, int j) { 42 if (board[i][j] != 'O') { 43 return; 44 } 45 Queue<Node> queue = new LinkedList<>(); 46 queue.offer(new Node(i, j)); 47 board[i][j] = visited; 48 int[] dx = {1, 0, -1, 0}; 49 int[] dy = {0, 1, 0, -1}; 50 while (!queue.isEmpty()) { 51 Node node = queue.poll(); 52 for (int k = 0; k < 4; k++) { 53 Node nextNode = new Node(node.x + dx[k], node.y + dy[k]); 54 if (inBound(nextNode, board) && board[nextNode.x][nextNode.y] == 'O') { 55 queue.offer(nextNode); 56 board[nextNode.x][nextNode.y] = visited; 57 } 58 } 59 } 60 } 61 public boolean inBound(Node node, char[][] board) { 62 int n = board.length; 63 int m = board[0].length; 64 if (node.x < 0 || node.x >= n) { 65 return false; 66 } 67 if (node.y < 0 || node.y >= m) { 68 return false; 69 } 70 return true; 71 } 72 }
思路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。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class UnionFind { 2 int[] parent; 3 public UnionFind(int n) { 4 parent = new int[n]; 5 for (int i = 0; i < n; i++) { 6 parent[i] = i; 7 } 8 } 9 public int find(int x) { 10 if (parent[x] == x) { 11 return x; 12 } 13 parent[x] = find(parent[x]); 14 return parent[x]; 15 } 16 public void union(int a, int b) { 17 int root_a = find(a); 18 int root_b = find(b); 19 if (root_a != root_b) { 20 if (root_a == parent.length - 1) { 21 parent[root_b] = root_a; 22 } else { 23 parent[root_a] = root_b; 24 } 25 } 26 } 27 } 28 public class Solution { 29 public void solve(char[][] board) { 30 if (board == null || board.length == 0 || board[0].length == 0) { 31 return; 32 } 33 int n = board.length; 34 int m = board[0].length; 35 UnionFind uf = new UnionFind(n * m + 1); 36 for (int i = 0; i < n; i++) { 37 for (int j = 0; j < m; j++) { 38 if (board[i][j] == 'X') { 39 continue; 40 } 41 if (i == 0 || i == n - 1 || j == 0 || j == m - 1) { 42 uf.union(i * m + j, n * m); 43 } else { 44 if (board[i - 1][j] == 'O') { 45 uf.union(i * m + j, (i - 1) * m + j); 46 } 47 if (board[i + 1][j] == 'O') { 48 uf.union(i * m + j, (i + 1) * m + j); 49 } 50 if (board[i][j - 1] == 'O') { 51 uf.union(i * m + j, i * m + j - 1); 52 } 53 if (board[i][j + 1] == 'O') { 54 uf.union(i * m + j, i * m + j + 1); 55 } 56 } 57 } 58 } 59 for (int i = 0; i < n; i++) { 60 for (int j = 0; j < m; j++) { 61 if (board[i][j] == 'X' || uf.find(i * m + j) == n * m) { 62 continue; 63 } 64 board[i][j] = 'X'; 65 } 66 } 67 } 68 }
Advanced Data Structure -- Trie
Implement Trie
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Implement a trie with insert, search, and startsWith methods. Notice You may assume that all inputs are consist of lowercase letters a-z. Have you met this question in a real interview? Yes Example insert("lintcode") search("code") >>> false startsWith("lint") >>> true startsWith("linterror") >>> false insert("linterror") search("lintcode) >>> true startsWith("linterror") >>> true
思路:trie树的插入,查找,前最匹配。需要注意一点的就是存储子节点的HashMap不能再构造方法里初始化,而是要在定义的时候直接初始化,这是为了覆盖dummy root的HashMap子节点情况。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Your Trie object will be instantiated and called as such: 3 * Trie trie = new Trie(); 4 * trie.insert("lintcode"); 5 * trie.search("lint"); will return false 6 * trie.startsWith("lint"); will return true 7 */ 8 class TrieNode { 9 char c; 10 Map<Character, TrieNode> children = new HashMap<>(); 11 boolean hasWord; 12 // Initialize your data structure here. 13 public TrieNode() { 14 } 15 public TrieNode(char c) { 16 this.c = c; 17 } 18 } 19 20 public class Trie { 21 private TrieNode root; 22 23 public Trie() { 24 root = new TrieNode(); 25 } 26 27 // Inserts a word into the trie. 28 public void insert(String word) { 29 TrieNode cur = root; 30 char[] wordArray = word.toCharArray(); 31 for (int i = 0; i < wordArray.length; i++) { 32 char temp = wordArray[i]; 33 if (cur.children.containsKey(temp)) { 34 cur = cur.children.get(temp); 35 } else { 36 TrieNode newNode = new TrieNode(temp); 37 cur.children.put(temp, newNode); 38 cur = newNode; 39 } 40 if (i == wordArray.length - 1) { 41 cur.hasWord = true; 42 } 43 } 44 } 45 46 // Returns if the word is in the trie. 47 public boolean search(String word) { 48 TrieNode node = searchWordNodePos(word); 49 if (node == null) { 50 return false; 51 } 52 if (node.hasWord) { 53 return true; 54 } 55 return false; 56 } 57 58 // Returns if there is any word in the trie 59 // that starts with the given prefix. 60 public boolean startsWith(String prefix) { 61 if (searchWordNodePos(prefix) == null) { 62 return false; 63 } 64 return true; 65 } 66 public TrieNode searchWordNodePos(String s) { 67 TrieNode cur = root; 68 char[] sArray = s.toCharArray(); 69 for (int i = 0; i < sArray.length; i++) { 70 char temp = sArray[i]; 71 if (!cur.children.containsKey(temp)) { 72 return null; 73 } 74 cur = cur.children.get(temp); 75 } 76 return cur; 77 } 78 }
Add and Search Word
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Design a data structure that supports the following two operations: addWord(word) and search(word) search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter. Notice You may assume that all words are consist of lowercase letters a-z. Have you met this question in a real interview? Yes Example addWord("bad") addWord("dad") addWord("mad") search("pad") // return false search("bad") // return true search(".ad") // return true search("b..") // return true
思路:类似上题,trie树的添加可以更加简化。查找单词要递归。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class TrieNode { 2 Map<Character, TrieNode> children; 3 boolean hasWord; 4 public TrieNode() { 5 children = new HashMap<>(); 6 hasWord = false; 7 } 8 } 9 public class WordDictionary { 10 TrieNode root; 11 public WordDictionary() { 12 root = new TrieNode(); 13 } 14 // Adds a word into the data structure. 15 public void addWord(String word) { 16 // Write your code here 17 TrieNode cur = root; 18 char[] wordArray = word.toCharArray(); 19 for (int i = 0; i < wordArray.length; i++) { 20 char temp = wordArray[i]; 21 if (!cur.children.containsKey(temp)) { 22 cur.children.put(temp, new TrieNode()); 23 } 24 cur = cur.children.get(temp); 25 } 26 cur.hasWord = true; 27 } 28 29 // Returns if the word is in the data structure. A word could 30 // contain the dot character '.' to represent any one letter. 31 public boolean search(String word) { 32 // Write your code here 33 return find(word, 0, root); 34 } 35 public boolean find(String word, int index, TrieNode cur) { 36 if (index == word.length()) { 37 return cur.hasWord; 38 } 39 char c = word.charAt(index); 40 if (cur.children.containsKey(c)) { 41 return find(word, index + 1, cur.children.get(c)); 42 } else if (c == '.') { 43 for (char temp : cur.children.keySet()) { 44 if (find(word, index + 1, cur.children.get(temp))) { 45 return true; 46 } 47 } 48 return false; 49 } else { 50 return false; 51 } 52 } 53 } 54 55 // Your WordDictionary object will be instantiated and called as such: 56 // WordDictionary wordDictionary = new WordDictionary(); 57 // wordDictionary.addWord("word"); 58 // wordDictionary.search("pattern");
Word Search
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a 2D board and a word, find if the word exists in the grid. The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once. Have you met this question in a real interview? Yes Example Given board = [ "ABCE", "SFCS", "ADEE" ] word = "ABCCED", -> returns true, word = "SEE", -> returns true, word = "ABCB", -> returns false.
思路:DFS 记得dfs的过程中把访问过的字符标记称某一特殊字符来表示访问过这一字符,避免重复访问。
time:O(n*m*4^len) space:O(len) + O(n*m)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param board: A list of lists of character 4 * @param word: A string 5 * @return: A boolean 6 */ 7 public boolean exist(char[][] board, String word) { 8 // write your code here 9 if (board == null || board.length == 0 || board[0].length == 0) { 10 return false; 11 } 12 if (word == null || word.length() == 0) { 13 return true; 14 } 15 int n = board.length; 16 int m = board[0].length; 17 for (int i = 0; i < n; i++) { 18 for (int j = 0; j < m; j++) { 19 if (board[i][j] == word.charAt(0)) { 20 boolean flag = dfsHelper(board, i, j, word, 0); 21 if (flag) { 22 return true; 23 } 24 } 25 } 26 } 27 return false; 28 } 29 public boolean dfsHelper(char[][] board, int i, int j, String word, int start) { 30 if (start == word.length()) { 31 return true; 32 } 33 int n = board.length; 34 int m = board[0].length; 35 if (i < 0 || i >= n || j < 0 || j >= m) { 36 return false; 37 } 38 if (board[i][j] != word.charAt(start)) { 39 return false; 40 } 41 board[i][j] = '#'; 42 boolean flag = dfsHelper(board, i + 1, j, word, start + 1) 43 || dfsHelper(board, i - 1, j, word, start + 1) 44 || dfsHelper(board, i, j + 1, word, start + 1) 45 || dfsHelper(board, i, j - 1, word, start + 1); 46 board[i][j] = word.charAt(start); 47 return flag; 48 } 49 }
Word Search II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a matrix of lower alphabets and a dictionary. Find all words in the dictionary that can be found in the matrix. A word can start from any position in the matrix and go left/right/up/down to the adjacent position. Have you met this question in a real interview? Yes Example Given matrix: doaf agai dcan and dictionary: {"dog", "dad", "dgdg", "can", "again"} return {"dog", "dad", "can", "again"}
思路:1. 把字典建成Trie树。2. 用dfs的方法遍历矩阵,同时在Trie上搜索前缀是否存在。 3. 查询所有Trie里面有可能出现的字符。 注意找到string之后还要继续往下低轨而不是return,因为下面也可能有要找的string。同时TrieNode中多了一个字符串s来代表从根走到当前位置的字符串,也就是插入的字符串。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class TrieNode { 2 Map<Character, TrieNode> children; 3 boolean hasWord; 4 String s; 5 public TrieNode() { 6 children = new HashMap<>(); 7 hasWord = false; 8 s = ""; 9 } 10 } 11 class TrieTree { 12 TrieNode root; 13 public TrieTree() { 14 root = new TrieNode(); 15 } 16 public void insert(String word) { 17 TrieNode cur = root; 18 char[] wordArray = word.toCharArray(); 19 for (int i = 0; i < wordArray.length; i++) { 20 char temp = wordArray[i]; 21 if (!cur.children.containsKey(temp)) { 22 TrieNode newNode = new TrieNode(); 23 cur.children.put(temp, new TrieNode()); 24 } 25 cur = cur.children.get(temp); 26 } 27 cur.s = word; 28 cur.hasWord = true; 29 } 30 } 31 public class Solution { 32 /** 33 * @param board: A list of lists of character 34 * @param words: A list of string 35 * @return: A list of string 36 */ 37 public ArrayList<String> wordSearchII(char[][] board, ArrayList<String> words) { 38 // write your code here 39 ArrayList<String> result = new ArrayList<>(); 40 if (board == null || board.length == 0 || board[0].length == 0) { 41 return result; 42 } 43 if (words == null || words.size() == 0) { 44 return result; 45 } 46 TrieTree tree = new TrieTree(); 47 for (String word : words) { 48 tree.insert(word); 49 } 50 int n = board.length; 51 int m = board[0].length; 52 for (int i = 0; i < n; i++) { 53 for (int j = 0; j < m; j++) { 54 search(board, i, j, tree.root, result); 55 } 56 } 57 return result; 58 } 59 public void search(char[][] board, int i, int j, TrieNode root, 60 ArrayList<String> result) { 61 if (root.hasWord) { 62 if (!result.contains(root.s)) { 63 result.add(root.s); 64 } 65 //注意此处不能return!!!比如字典里有"se"和"see" 66 } 67 if (i < 0 || i >= board.length || j < 0 || j >= board[0].length) { 68 return; 69 } 70 if (!root.children.containsKey(board[i][j])) { 71 return; 72 } 73 int[] dx = {1, 0, -1, 0}; 74 int[] dy = {0, 1, 0, -1}; 75 for (int k = 0; k < 4; k++) { 76 char temp = board[i][j]; 77 board[i][j] = '#'; 78 search(board, i + dx[k], j + dy[k], root.children.get(temp), result); 79 board[i][j] = temp; 80 } 81 } 82 }
Word Squares
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a set of words without duplicates, find all word squares you can build from them. A sequence of words forms a valid word square if the kth row and column read the exact same string, where 0 ≤ k < max(numRows, numColumns). For example, the word sequence ["ball","area","lead","lady"] forms a word square because each word reads the same both horizontally and vertically. b a l l a r e a l e a d l a d y Notice There are at least 1 and at most 1000 words. All words will have the exact same length. Word length is at least 1 and at most 5. Each word contains only lowercase English alphabet a-z. Have you met this question in a real interview? Yes Example Given a set ["area","lead","wall","lady","ball"] return [["wall","area","lead","lady"],["ball","area","lead","lady"]] Explanation: The output consists of two word squares. The order of output does not matter (just the order of words in each word square matters). Given a set ["abat","baba","atan","atal"] return [["baba","abat","baba","atan"],["baba","abat","baba","atal"]] Explanation: The output consists of two word squares. The order of output does not matter (just the order of words in each word square matters).
思路:Trie + DFS。先构建trie,新增一个List<String> startWith属性表示包含以当前字符结尾的前缀的所有单词的集合。然后DFS遍历,注意下一个可能构成word squares的单词的前缀是由前面的单词严格限制的,也就是说下一个单子的前缀是固定的,在trie中找到包含这个前缀的所有可能的单词(startWith),对这些单词进行下一轮DFS。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class TrieNode { 2 Map<Character, TrieNode> children; 3 List<String> startWith; 4 public TrieNode() { 5 children = new HashMap<>(); 6 startWith = new ArrayList<>(); 7 } 8 } 9 class Trie { 10 TrieNode root; 11 public Trie() { 12 root = new TrieNode(); 13 } 14 public void insert(String word) { 15 TrieNode cur = root; 16 char[] wordArray = word.toCharArray(); 17 for (char c : wordArray) { 18 if (!cur.children.containsKey(c)) { 19 cur.children.put(c, new TrieNode()); 20 } 21 cur.children.get(c).startWith.add(word); 22 cur = cur.children.get(c); 23 } 24 } 25 public List<String> findByPrefix(String prefix) { 26 TrieNode cur = root; 27 char[] prefixArray = prefix.toCharArray(); 28 for (char c : prefixArray) { 29 if (!cur.children.containsKey(c)) { 30 return new ArrayList<String>(); 31 } 32 cur = cur.children.get(c); 33 } 34 return cur.startWith; 35 } 36 } 37 public class Solution { 38 /** 39 * @param words a set of words without duplicates 40 * @return all word squares 41 */ 42 public List<List<String>> wordSquares(String[] words) { 43 // Write your code here 44 List<List<String>> results = new ArrayList<>(); 45 if (words == null || words.length == 0) { 46 return results; 47 } 48 int n = words[0].length(); 49 Trie trie = new Trie(); 50 for (String word : words) { 51 trie.insert(word); 52 } 53 List<String> tempList = new ArrayList<>(); 54 for (String word : words) { 55 tempList.add(word); 56 search(trie, tempList, n, results); 57 tempList.remove(tempList.size() - 1); 58 } 59 return results; 60 } 61 public void search(Trie trie, List<String> tempList, int n, 62 List<List<String>> results) { 63 if (tempList.size() == n) { 64 results.add(new ArrayList<>(tempList)); 65 return; 66 } 67 StringBuilder sb = new StringBuilder(); 68 int index = tempList.size(); 69 for (String word : tempList) { 70 sb.append(word.charAt(index)); 71 } 72 List<String> startWith = trie.findByPrefix(sb.toString()); 73 for (String sw : startWith) { 74 tempList.add(sw); 75 search(trie, tempList, n, results); 76 tempList.remove(tempList.size() - 1); 77 } 78 } 79 }
Boggle Game
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a board which is a 2D matrix includes a-z and dictionary dict, find the largest collection of words on the board, the words can not overlap in the same position. return the size of largest collection. Have you met this question in a real interview? Yes Example Give a board below [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']] dict = ["abc", "cfi", "beh", "defi", "gh"] Return 3 // we can get the largest collection["abc", "defi", "gh"]
思路:trie + DFS 不得不说这道题真的难,搞了半天。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class TrieNode { 2 Map<Character, TrieNode> children; 3 boolean isWord; 4 public TrieNode() { 5 children = new HashMap<>(); 6 isWord = false; 7 } 8 } 9 class Trie { 10 TrieNode root; 11 public Trie() { 12 root = new TrieNode(); 13 } 14 public void insert(String word) { 15 TrieNode cur = root; 16 char[] wordArray = word.toCharArray(); 17 for (char c : wordArray) { 18 if (!cur.children.containsKey(c)) { 19 cur.children.put(c, new TrieNode()); 20 } 21 cur = cur.children.get(c); 22 } 23 cur.isWord = true; 24 } 25 } 26 class Point { 27 int x; 28 int y; 29 public Point(int x, int y) { 30 this.x = x; 31 this.y = y; 32 } 33 } 34 public class Solution { 35 /** 36 * @param board a list of lists of character 37 * @param words a list of string 38 * @return an integer 39 */ 40 int max = 0; 41 int n = 0; 42 int m = 0; 43 TrieNode globalRoot; 44 int[] dx = {1, 0, -1, 0}; 45 int[] dy = {0, 1, 0, -1}; 46 public int boggleGame(char[][] board, String[] words) { 47 // Write your code here 48 if (board == null || board.length == 0 || board[0].length == 0 49 || words == null || words.length == 0) { 50 return 0; 51 } 52 n = board.length; 53 m = board[0].length; 54 Trie trie = new Trie(); 55 globalRoot = trie.root; 56 for (String word : words) { 57 trie.insert(word); 58 } 59 for (int i = 0; i < n; i++) { 60 for (int j = 0; j < m; j++) { 61 if (trie.root.children.containsKey(board[i][j])) { 62 boolean[][] visited = new boolean[n][m]; 63 visited[i][j] = true; 64 dfsHelper(board, i, j, trie.root.children.get(board[i][j]), visited, 0); 65 if (max == n * m) { 66 break; 67 } 68 } 69 } 70 } 71 return max; 72 } 73 public void dfsHelper(char[][] board, int x, int y, TrieNode cur, boolean[][] visited, int count) { 74 if (cur.isWord) { 75 count++; 76 max = Math.max(max, count); 77 List<Point> visitedPoint = new ArrayList<>(); 78 for (int i = 0; i < n; i++) { 79 for (int j = 0; j < m; j++) { 80 if (!visited[i][j] 81 && globalRoot.children.containsKey(board[i][j])) { 82 visited[i][j] = true; 83 visitedPoint.add(new Point(i, j)); 84 dfsHelper(board, i, j, 85 globalRoot.children.get(board[i][j]), visited, count); 86 } 87 } 88 } 89 for (Point point : visitedPoint) { 90 visited[point.x][point.y] = false; 91 } 92 } else { 93 for (int k = 0; k < 4; k++) { 94 int next_x = x + dx[k]; 95 int next_y = y + dy[k]; 96 if (validPoint(next_x, next_y) && !visited[next_x][next_y] 97 && cur.children.containsKey(board[next_x][next_y])) { 98 visited[next_x][next_y] = true; 99 dfsHelper(board, next_x, next_y, 100 cur.children.get(board[next_x][next_y]), visited, count); 101 visited[next_x][next_y] = false; 102 } 103 } 104 } 105 } 106 public boolean validPoint(int x, int y) { 107 return x >= 0 && x < n && y >= 0 && y < m; 108 } 109 }
139. Word Break
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words. You may assume the dictionary does not contain duplicate words. For example, given s = "leetcode", dict = ["leet", "code"]. Return true because "leetcode" can be segmented as "leet code". UPDATE (2017/1/4): The wordDict 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.
思路:DP 两种DP方法,第一种是j从0遍历到i- 1,时间复杂度O(len^2),len为字符串s的长度。适用于字典大,s短的情况。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean wordBreak(String s, List<String> wordDict) { 3 if (s == null || s.length() == 0) { 4 return false; 5 } 6 if (wordDict == null || wordDict.size() == 0) { 7 return false; 8 } 9 // 1.状态定义 10 boolean[] canSegment = new boolean[s.length() + 1]; 11 // 2.初始化 12 canSegment[0] = true; 13 // 3.循环递归求解 14 for (int i = 0; i < s.length() + 1; i++) { 15 for (int j = 0; j < i; j++) { 16 if (!canSegment[j]) { 17 continue; 18 } 19 if (wordDict.contains(s.substring(j, i))) { 20 canSegment[i] = true; 21 break; 22 } 23 } 24 } 25 // 4. 求结果 26 return canSegment[s.length()]; 27 } 28 }
第二种是遍历字典中的每个单词的长度,如果canSegment[i - word.length()] == true && s.substring(i - word.length(), i).equals(当前遍历的单词),那么canSegment[i]为true。时间复杂度O(len * size),len为字符串s的长度,size为字典的大小。适用于字典小,s大的情况。而且本题中字典为list而不是set,判断list是否含有某个元素的contains方法时间复杂度是O(size)的不是O(1),所以建议使用第二种方法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public boolean wordBreak(String s, List<String> wordDict) { 3 if (s == null || s.length() == 0) { 4 return false; 5 } 6 if (wordDict == null || wordDict.size() == 0) { 7 return false; 8 } 9 // 1.状态定义 10 boolean[] canSegment = new boolean[s.length() + 1]; 11 // 2.初始化 12 canSegment[0] = true; 13 // 3.循环递归求解 14 for (int i = 0; i < s.length() + 1; i++) { 15 for (String word : wordDict) { 16 if (i - word.length() < 0 || !canSegment[i - word.length()]) { 17 continue; 18 } 19 if (s.substring(i - word.length(), i).equals(word)) { 20 canSegment[i] = true; 21 break; 22 } 23 } 24 } 25 // 4. 求结果 26 return canSegment[s.length()]; 27 } 28 }
140. Word Break II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. You may assume the dictionary does not contain duplicate words. Return all such possible sentences. For example, given s = "catsanddog", dict = ["cat", "cats", "and", "sand", "dog"]. A solution is ["cats and dog", "cat sand dog"]. UPDATE (2017/1/4): The wordDict 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.
思路:和上一题不同的是,这道题要返回所有可能的组合。所以现在dp[i]里面应该存可以使长度为i所有可能的String里的最后一个word。dp有两种写法,一个就是直接写成数组:List[]的形式,不能形成的dp[i] = null。还有一个是用个hashmap:Map<Integer, List>,用hashmap的好处是如果s很长而且用dict能组合成的长度不是很多的话,map用的空间相对少。dp结束之后,第二步就是通过dp里面保存的word,一步一步回溯找到所有结果。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<String> wordBreak(String s, List<String> wordDict) { 3 List<String> result = new ArrayList<>(); 4 if (s == null || s.length() == 0 || wordDict == null || wordDict.size() == 0) { 5 return result; 6 } 7 Map<Integer, List<String>> dp = new HashMap<>(); 8 dp.put(0, new ArrayList<>()); 9 for (int i = 0; i <= s.length(); i++) { 10 for (String word : wordDict) { 11 int j = i - word.length(); 12 if (j < 0 || !dp.containsKey(j)) { 13 continue; 14 } 15 if (s.substring(j, i).equals(word)) { 16 if (!dp.containsKey(i)) { 17 dp.put(i, new ArrayList<>()); 18 } 19 dp.get(i).add(word); 20 } 21 } 22 } 23 if (!dp.containsKey(s.length())) { 24 return result; 25 } 26 dfsHelper(dp, s.length(), "", result); 27 return result; 28 } 29 public void dfsHelper(Map<Integer, List<String>> dp, int startIndex, String path, List<String> result) { 30 if (startIndex == 0) { 31 result.add(path); 32 return; 33 } 34 for (String word : dp.get(startIndex)) { 35 dfsHelper(dp, startIndex - word.length(), word + (path.equals("") ? "" : " " + path), result); 36 } 37 } 38 }
472. Concatenated Words
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a list of words (without duplicates), please write a program that returns all concatenated words in the given list of words. A concatenated word is defined as a string that is comprised entirely of at least two shorter words in the given array. Example: Input: ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"] Output: ["catsdogcats","dogcatsdog","ratcatdogcat"] Explanation: "catsdogcats" can be concatenated by "cats", "dog" and "cats"; "dogcatsdog" can be concatenated by "dog", "cats" and "dog"; "ratcatdogcat" can be concatenated by "rat", "cat", "dog" and "cat". Note: The number of elements of the given array will not exceed 10,000 The length sum of elements in the given array will not exceed 600,000. All the input string will only include lower case letters. The returned elements order does not matter.
思路I:DP 常规思路是遍历数组中的每一个单词,然后对该单词,查看该单词是否能右数组中剩余的元素组成,这样做时间复杂度过高。需要进行优化,一个单词要想被其他词组成,其他词的长度必然小于这个词的长度,于是先对数组按各单词长度进行从小到大排序,然后每次遍历过程中,用一个set来存储比当前单词长度小的单词,也就是说set存储的是能组成当前单词的候选单词,在遍历过程中如果当前单词能由set里的单词组成(这个过程借用Word Break这道题的DP思路),就把当前单词加入到结果中。最后循环结束就可以返回结果了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<String> findAllConcatenatedWordsInADict(String[] words) { 3 List<String> result = new ArrayList<>(); 4 if (words == null || words.length < 3) { 5 return result; 6 } 7 Arrays.sort(words, new Comparator<String>() { 8 public int compare(String a, String b) { 9 return a.length() - b.length(); 10 } 11 }); 12 Set<String> set = new HashSet<>(); 13 for (int i = 0; i < words.length; i++) { 14 if (wordBreak(words[i], set)) { 15 result.add(words[i]); 16 } 17 set.add(words[i]); 18 } 19 return result; 20 } 21 public boolean wordBreak(String word, Set<String> set) { 22 if (set.isEmpty() || set.size() == 0) { 23 return false; 24 } 25 // 递归四要素 26 // 1.状态定义 27 boolean[] canSegment = new boolean[word.length() + 1]; 28 // 2.初始化 29 canSegment[0] = true; 30 // 3.循环递归求解 31 for (int i = 1; i < canSegment.length; i++) { 32 for (int j = 0; j < i; j++) { 33 if (!canSegment[j]) { 34 continue; 35 } 36 if (set.contains(word.substring(j, i))) { 37 canSegment[i] = true; 38 break; 39 } 40 } 41 } 42 // 4.求结果 43 return canSegment[word.length()]; 44 } 45 }
思路II:trie Trie 先将各个单词插入到Trie树,然后对每个单词,判断它是否能由其他单词组成。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class TrieNode { 2 Map<Character, TrieNode> children; 3 boolean hasWord; 4 public TrieNode() { 5 children = new HashMap<>(); 6 hasWord = false; 7 } 8 } 9 class Trie { 10 TrieNode root; 11 public Trie() { 12 root = new TrieNode(); 13 } 14 public void insert(String word) { 15 char[] wordArray = word.toCharArray(); 16 TrieNode cur = root; 17 for (int i = 0; i < wordArray.length; i++) { 18 char temp = wordArray[i]; 19 if (!cur.children.containsKey(temp)) { 20 cur.children.put(temp, new TrieNode()); 21 } 22 cur = cur.children.get(temp); 23 } 24 cur.hasWord = true; 25 } 26 } 27 public class Solution { 28 public List<String> findAllConcatenatedWordsInADict(String[] words) { 29 List<String> result = new ArrayList<>(); 30 if (words == null || words.length <= 1) { 31 return result; 32 } 33 Trie trie = new Trie(); 34 for (String word : words) { 35 trie.insert(word); 36 } 37 for (String word : words) { 38 if (dfsHelper(word.toCharArray(), 0, trie.root, 0)) { 39 result.add(word); 40 } 41 } 42 return result; 43 } 44 public boolean dfsHelper(char[] chars, int startIndex, TrieNode root, int count) { 45 TrieNode cur = root; 46 for (int i = startIndex; i < chars.length; i++) { 47 if (!cur.children.containsKey(chars[i])) { 48 return false; 49 } 50 cur = cur.children.get(chars[i]); 51 if (cur.hasWord) { 52 if (i == chars.length - 1) { 53 return count >= 1; 54 } 55 if (dfsHelper(chars, i + 1, root, count + 1)) { 56 return true; 57 } 58 } 59 } 60 return false; 61 } 62 }
Find the Weak Connected Component in the Directed Graph
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Find the number Weak Connected Component in the directed graph. Each node in the graph contains a label and a list of its neighbors. (a connected set of a directed graph is a subgraph in which any two vertices are connected by direct edge path.) Notice Sort the element in the set in increasing order Have you met this question in a real interview? Yes Example Given graph: A----->B C \ | | \ | | \ | | \ v v ->D E <- F Return {A,B,D}, {C,E,F}. Since there are two connected component which are {A,B,D} and {C,E,F}
思路:并查集 由于节点的值不确定,所以用HashMap来存储一个节点和它的父节点的映射关系而不是用数组存储,注意用HashMap来存储的时候并查集中的find函数的写法!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for Directed graph. 3 * class DirectedGraphNode { 4 * int label; 5 * ArrayList<DirectedGraphNode> neighbors; 6 * DirectedGraphNode(int x) { label = x; neighbors = new ArrayList<DirectedGraphNode>(); } 7 * }; 8 */ 9 class UnionFind { 10 Map<Integer, Integer> map; 11 public UnionFind(HashSet<Integer> hash) { 12 map = new HashMap<>(); 13 for (Integer temp : hash) { 14 map.put(temp, temp); 15 } 16 } 17 public int find(int x) { 18 if (map.get(x) == x) { 19 return x; 20 } 21 map.put(x, find(map.get(x))); 22 return map.get(x); 23 } 24 public void union(int a, int b) { 25 int root_a = find(a); 26 int root_b = find(b); 27 if (root_a != root_b) { 28 map.put(root_a, root_b); 29 } 30 } 31 } 32 public class Solution { 33 /** 34 * @param nodes a array of Directed graph node 35 * @return a connected set of a directed graph 36 */ 37 public List<List<Integer>> connectedSet2(ArrayList<DirectedGraphNode> nodes) { 38 // Write your code here 39 List<List<Integer>> results = new ArrayList<>(); 40 if (nodes == null || nodes.size() == 0) { 41 return results; 42 } 43 HashSet<Integer> set = new HashSet<>(); 44 for (DirectedGraphNode node : nodes) { 45 set.add(node.label); 46 } 47 UnionFind uf = new UnionFind(set); 48 for (DirectedGraphNode node : nodes) { 49 for (DirectedGraphNode neighbor : node.neighbors) { 50 uf.union(node.label, neighbor.label); 51 } 52 } 53 HashMap<Integer, ArrayList<Integer>> children = new HashMap<Integer, ArrayList<Integer>>(); 54 for (int num : set) { 55 int root_num = uf.find(num); 56 if (!children.containsKey(root_num)) { 57 children.put(root_num, new ArrayList<Integer>()); 58 } 59 children.get(root_num).add(num); 60 } 61 for (Integer father : children.keySet()) { 62 Collections.sort(children.get(father)); 63 results.add(children.get(father)); 64 } 65 return results; 66 } 67 }
Sweep Line
Building Outline
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Question Given N buildings in a x-axis,each building is a rectangle and can be represented by a triple (start, end, height),where start is the start position on x-axis, end is the end position on x-axis and height is the height of the building. Buildings may overlap if you see them from far away,find the outline of them。 An outline can be represented by a triple, (start, end, height), where start is the start position on x-axis of the outline, end is the end position on x-axis and height is the height of the outline. Notice Please merge the adjacent outlines if they have the same height and make sure different outlines cant overlap on x-axis. Example Given 3 buildings: [ [1, 3, 3], [2, 4, 4], [5, 6, 1] ] The outlines are: [ [1, 2, 3], [2, 4, 4], [5, 6, 1] ]
思路I:使用PriorityQueue
我的解法:用一个带最大堆的扫描线遍历数组,每出现一个拐点则记录一次区间。新加入一个元素后若堆顶元素和之前不同,则表明出现拐点。
-
首先处理数组中元素。将每一个点用一个point来保存,保存time(开始写错了,应该是位置,但是要改的太多了),flag(起点为1,终点为0),height。用一个HashMap来记录每一对终点和起点(终点为key,起点为value)。
-
将所有point保存在一个list中并排序,首先根据时间从小到大排序,若时间相等则根据flag排序(先起点后终点),若flag也相等则根据height排序(若同为起点则从大到小排序,若同为终点则从小到大排序)。这样可以避免重复解。
-
再构建一个最大堆,用于记录加入的元素的最大值。
-
开始遍历list中每个point,起点元素加入堆,终点元素删去堆中对应的起点元素。
-
当遇到一个起点元素时,先记录加入前堆顶元素,然后将该起点元素加入,再看加入后堆顶元素,1)若没有变化,则继续下一个point;2)若有变化,则说明出现拐点,将之前堆顶元素时间作为起点,当前堆顶元素时间作为终点,之前堆顶元素高度作为高度。注意:就算堆顶元素变化,但是如果之前堆顶元素和当前堆顶元素时间相同,说明是在这个时间连续有几个起点被加入,宽度为0,不能算一个区间。
-
当遇到一个终点元素时,将其对应的起点元素从堆中删除。若此时堆为空,则和5中一样记录一个区间,并继续下一个point。若堆不为空,则需要看此时堆顶元素是否改变。若不变则继续,否则说明出现“拐点”。此处“拐点”要分两种情况讨论: 1)若新的堆顶元素高度和之前堆顶元素高度相同,则说明相同高度的两段区间有重叠,题目要求若发生这种情况要合并这两段区间,所以我们要保留之前的堆顶元素(两段同高度相同重叠区间的最左边),删去新的堆顶元素(即代替原堆顶元素被删除,因为每遇到一个终点必须删去一个起点),具体做法可以是先删去新堆顶元素,再加入原堆顶元素,或者直接将新堆顶元素时间改为原堆顶元素时间。 2)若新堆顶和原堆顶元素高度不同,则像5中那样记录一个区间,但是要将现在的堆顶元素时间改为遇到的终点元素的时间。
-
遍历完整个list结束
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Point { 2 int index; 3 int height; 4 int flag; 5 public Point(int index, int height, int flag) { 6 this.index = index; 7 this.height = height; 8 this.flag = flag; 9 } 10 } 11 public class Solution { 12 /** 13 * @param buildings: A list of lists of integers 14 * @return: Find the outline of those buildings 15 */ 16 public ArrayList<ArrayList<Integer>> buildingOutline(int[][] buildings) { 17 // write your code here 18 ArrayList<ArrayList<Integer>> result = new ArrayList<>(); 19 if (buildings == null || buildings.length == 0 20 || buildings[0].length == 0) { 21 return result; 22 } 23 List<Point> points = new ArrayList<>(); 24 Map<Point, Point> map = new HashMap<>(); 25 for (int[] building : buildings) { 26 Point start = new Point(building[0], building[2], 1); 27 Point end = new Point(building[1], building[2], 0); 28 points.add(start); 29 points.add(end); 30 map.put(end, start); 31 } 32 Collections.sort(points, new Comparator<Point>() { 33 public int compare(Point a, Point b) { 34 if (a.index != b.index) { 35 return a.index - b.index; 36 } else if (a.flag != b.flag) { 37 return b.flag - a.flag; 38 } else { 39 if (a.flag == 1) { 40 return b.height - a.height; 41 } 42 return a.height - b.height; 43 } 44 } 45 }); 46 Queue<Point> maxHeap = new PriorityQueue<>(buildings.length, new Comparator<Point>() { 47 public int compare(Point a, Point b) { 48 if (a.height != b.height) { 49 return b.height - a.height; 50 } 51 return a.index - b.index; 52 } 53 }); 54 for (int i = 0; i < points.size(); i++) { 55 if (maxHeap.isEmpty()) { 56 maxHeap.offer(points.get(i)); 57 continue; 58 } 59 Point pre = maxHeap.peek(); 60 Point next = points.get(i); 61 ArrayList<Integer> tempList = new ArrayList<>(); 62 if (next.flag == 1) { 63 maxHeap.offer(next); 64 Point cur = maxHeap.peek(); 65 //由于points排序规则的作用,不会出现当堆顶节点变化时老堆顶节点的索引等于新堆顶节点的索引 66 if (cur != pre && cur.index != pre.index) { 67 tempList.add(pre.index); 68 tempList.add(cur.index); 69 tempList.add(pre.height); 70 result.add(tempList); 71 } 72 } else { 73 maxHeap.remove(map.get(next)); 74 if (maxHeap.isEmpty()) { 75 tempList.add(pre.index); 76 tempList.add(next.index); 77 tempList.add(pre.height); 78 result.add(tempList); 79 continue; 80 } 81 Point cur = maxHeap.peek(); 82 if (cur != pre) { 83 if (cur.height == pre.height) { 84 cur.index = pre.index; 85 } else { 86 tempList.add(pre.index); 87 tempList.add(next.index); 88 tempList.add(pre.height); 89 result.add(tempList); 90 cur.index = next.index; 91 } 92 } 93 } 94 } 95 return result; 96 } 97 }
思路II:自定义HashHeap堆可以将PriorityQueue中O(n)的remove操作优化到O(lgn)的remove操作。注意点已经在代码中标出。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Node { 2 int time; 3 int height; 4 int flag; 5 public Node(int time, int height, int flag) { 6 this.time = time; 7 this.height = height; 8 this.flag = flag; 9 } 10 } 11 class HashHeap { 12 List<Node> list; 13 Map<Node, Integer> map; 14 public HashHeap() { 15 list = new ArrayList<>(); 16 map = new HashMap<>(); 17 } 18 public int getParent(int index) { 19 if (index == 0) { 20 return -1; 21 } 22 return (index - 1) / 2; 23 } 24 public int getLeftChild(int index) { 25 return 2 * index + 1; 26 } 27 public int getRightChild(int index) { 28 return 2 * index + 2; 29 } 30 public int size() { 31 return list.size(); 32 } 33 public Node peek() { 34 return list.get(0); 35 } 36 public void offer(Node node) { 37 list.add(node); 38 map.put(list.get(list.size() - 1), list.size() - 1); 39 siftup(list.size() - 1); 40 } 41 public Node poll() { 42 if (list.size() == 0) { 43 return null; 44 } 45 Node temp = list.get(0); 46 list.set(0, list.get(list.size() - 1)); 47 list.set(list.size() - 1, temp); 48 map.put(list.get(0), 0); 49 map.put(list.get(list.size() - 1), list.size() - 1); 50 map.remove(list.get(list.size() - 1)); 51 list.remove(list.size() - 1); 52 siftdown(0); 53 return temp; 54 } 55 public void remove(Node node) { 56 int index = map.get(node); 57 // 注意:当删除的节点是最后一个节点时,此时需要调整的起始节点已经从堆里里面删除, 58 // 就不存在siftup和siftdown操作了!否则下标会越界! 59 if (index == list.size() - 1) { 60 map.remove(list.get(index)); 61 list.remove(index); 62 return; 63 } 64 Node temp = list.get(index); 65 list.set(index, list.get(list.size() - 1)); 66 list.set(list.size() - 1, temp); 67 map.put(list.get(index), index); 68 map.put(list.get(list.size() - 1), list.size() - 1); 69 map.remove(list.get(list.size() - 1)); 70 list.remove(list.size() - 1); 71 siftup(index); 72 siftdown(index); 73 } 74 public void siftup(int index) { 75 while (index >= 0) { 76 int smallestIndex = index; 77 int parentIndex = getParent(index); 78 if (parentIndex >= 0 79 && compare(list.get(index), list.get(parentIndex))) { 80 smallestIndex = parentIndex; 81 } 82 if (smallestIndex == index) { 83 break; 84 } 85 swap(index, parentIndex); 86 index = smallestIndex; 87 } 88 } 89 public void siftdown(int index) { 90 while (index < list.size()) { 91 int maximumIndex = index; 92 int leftIndex = getLeftChild(index); 93 if (leftIndex < list.size() 94 && compare(list.get(leftIndex), list.get(maximumIndex))) { 95 maximumIndex = leftIndex; 96 } 97 int rightIndex = getRightChild(index); 98 if (rightIndex < list.size() 99 && compare(list.get(rightIndex), list.get(maximumIndex))) { 100 maximumIndex = rightIndex; 101 } 102 if (maximumIndex == index) { 103 break; 104 } 105 swap(index, maximumIndex); 106 index = maximumIndex; 107 } 108 } 109 public boolean compare(Node a, Node b) { 110 if (a.height != b.height) { 111 return a.height > b.height; 112 } 113 return a.time < b.time; 114 } 115 public void swap(int index1, int index2) { 116 Node temp = list.get(index1); 117 list.set(index1, list.get(index2)); 118 list.set(index2, temp); 119 map.put(list.get(index1), index1); 120 map.put(list.get(index2), index2); 121 } 122 } 123 public class Solution { 124 /** 125 * @param buildings: A list of lists of integers 126 * @return: Find the outline of those buildings 127 */ 128 public ArrayList<ArrayList<Integer>> buildingOutline(int[][] buildings) { 129 // write your code here 130 ArrayList<ArrayList<Integer>> result = new ArrayList<>(); 131 if (buildings == null || buildings.length == 0 132 || buildings[0].length == 0) { 133 return result; 134 } 135 List<Node> nodes = new ArrayList<>(); 136 Map<Node, Node> map = new HashMap<>(); 137 for (int[] building : buildings) { 138 Node start = new Node(building[0], building[2], 1); 139 Node end = new Node(building[1], building[2], 0); 140 nodes.add(start); 141 nodes.add(end); 142 map.put(end, start); 143 } 144 Collections.sort(nodes, new Comparator<Node>() { 145 public int compare(Node a, Node b) { 146 if (a.time != b.time) { 147 return a.time - b.time; 148 } 149 if (a.flag != b.flag) { 150 return b.flag - a.flag; 151 } 152 if (a.flag == 1) { 153 return b.height - a.height; 154 } 155 return a.height - b.height; 156 } 157 }); 158 HashHeap maxHeap = new HashHeap(); 159 for (int i = 0; i < nodes.size(); i++) { 160 161 if (maxHeap.size() == 0) { 162 maxHeap.offer(nodes.get(i)); 163 continue; 164 } 165 Node pre = maxHeap.peek(); 166 Node next = nodes.get(i); 167 ArrayList<Integer> tempList = new ArrayList<>(); 168 if (next.flag == 1) { 169 maxHeap.offer(next); 170 Node cur = maxHeap.peek(); 171 if (cur != pre && cur.time != pre.time) { 172 tempList.add(pre.time); 173 tempList.add(cur.time); 174 tempList.add(pre.height); 175 result.add(tempList); 176 } 177 } else { 178 maxHeap.remove(map.get(next)); 179 if (maxHeap.size() == 0) { 180 tempList.add(pre.time); 181 tempList.add(next.time); 182 tempList.add(pre.height); 183 result.add(tempList); 184 continue; 185 } 186 Node cur = maxHeap.peek(); 187 if (pre != cur) { 188 if (cur.height == pre.height) { 189 cur.time = pre.time; 190 } else { 191 tempList.add(pre.time); 192 tempList.add(next.time); 193 tempList.add(pre.height); 194 result.add(tempList); 195 cur.time = next.time; 196 } 197 } 198 } 199 } 200 return result; 201 } 202 }
Advanced Data Structure -- Segment Tree
Segment Tree Build
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
The structure of Segment Tree is a binary tree which each node has two attributes start and end denote an segment / interval. start and end are both integers, they should be assigned in following rules: The root's start and end is given by build method. The left child of node A has start=A.left, end=(A.left + A.right) / 2. The right child of node A has start=(A.left + A.right) / 2 + 1, end=A.right. if start equals to end, there will be no children for this node. Implement a build method with two parameters start and end, so that we can create a corresponding segment tree with every node has the correct start and end value, return the root of this segment tree. Have you met this question in a real interview? Yes Clarification Segment Tree (a.k.a Interval Tree) is an advanced data structure which can support queries like: which of these intervals contain a given point which of these points are in a given interval See wiki: Segment Tree Interval Tree Example Given start=0, end=3. The segment tree will be: [0, 3] / \ [0, 1] [2, 3] / \ / \ [0, 0] [1, 1] [2, 2] [3, 3] Given start=1, end=6. The segment tree will be: [1, 6] / \ [1, 3] [4, 6] / \ / \ [1, 2] [3,3] [4, 5] [6,6] / \ / \ [1,1] [2,2] [4,4] [5,5]
思路:很简单,类似写建立二叉树的程序。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition of SegmentTreeNode: 3 * public class SegmentTreeNode { 4 * public int start, end; 5 * public SegmentTreeNode left, right; 6 * public SegmentTreeNode(int start, int end) { 7 * this.start = start, this.end = end; 8 * this.left = this.right = null; 9 * } 10 * } 11 */ 12 public class Solution { 13 /** 14 *@param start, end: Denote an segment / interval 15 *@return: The root of Segment Tree 16 */ 17 public SegmentTreeNode build(int start, int end) { 18 // write your code here 19 if (start > end) { 20 return null; 21 } 22 if (start == end) { 23 return new SegmentTreeNode(start, start); 24 } 25 SegmentTreeNode root = new SegmentTreeNode(start, end); 26 int mid = start + (end - start) / 2; 27 root.left = build(start, mid); 28 root.right = build(mid + 1, end); 29 return root; 30 } 31 }
Segment Tree Build II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
The structure of Segment Tree is a binary tree which each node has two attributes start and end denote an segment / interval. start and end are both integers, they should be assigned in following rules: The root's start and end is given by build method. The left child of node A has start=A.left, end=(A.left + A.right) / 2. The right child of node A has start=(A.left + A.right) / 2 + 1, end=A.right. if start equals to end, there will be no children for this node. Implement a build method with a given array, so that we can create a corresponding segment tree with every node value represent the corresponding interval max value in the array, return the root of this segment tree. Have you met this question in a real interview? Yes Clarification Segment Tree (a.k.a Interval Tree) is an advanced data structure which can support queries like: which of these intervals contain a given point which of these points are in a given interval See wiki: Segment Tree Interval Tree Example Given [3,2,1,4]. The segment tree will be: [0, 3] (max = 4) / \ [0, 1] (max = 3) [2, 3] (max = 4) / \ / \ [0, 0](max = 3) [1, 1](max = 2)[2, 2](max = 1) [3, 3] (max = 4)
思路:多了求每个区间的最大值,navie做法是每次建立某一区间节点,就调用findMax()函数在数组中的该区间内找最大值,这个没必要,我们可以从左右区间子树的max属性来更新当前区间节点的max。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition of SegmentTreeNode: 3 * public class SegmentTreeNode { 4 * public int start, end, max; 5 * public SegmentTreeNode left, right; 6 * public SegmentTreeNode(int start, int end, int max) { 7 * this.start = start; 8 * this.end = end; 9 * this.max = max 10 * this.left = this.right = null; 11 * } 12 * } 13 */ 14 public class Solution { 15 /** 16 *@param A: a list of integer 17 *@return: The root of Segment Tree 18 */ 19 public SegmentTreeNode build(int[] A) { 20 // write your code here 21 if (A == null || A.length == 0) { 22 return null; 23 } 24 return buildHelper(A, 0, A.length - 1); 25 } 26 public SegmentTreeNode buildHelper(int[] A, int start, int end) { 27 if (start > end) { 28 return null; 29 } 30 if (start == end) { 31 return new SegmentTreeNode(start, start, A[start]); 32 } 33 int mid = start + (end - start) / 2; 34 SegmentTreeNode root = new SegmentTreeNode(start, end, A[start]); 35 root.left = buildHelper(A, start, mid); 36 root.right = buildHelper(A, mid + 1, end); 37 if (root.left != null && root.left.max > root.max) { 38 root.max = root.left.max; 39 } 40 if (root.right != null && root.right.max > root.max) { 41 root.max = root.right.max; 42 } 43 return root; 44 } 45 }
Segment Tree Query
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
For an integer array (index from 0 to n-1, where n is the size of this array), in the corresponding SegmentTree, each node stores an extra attribute max to denote the maximum number in the interval of the array (index from start to end). Design a query method with three parameters root, start and end, find the maximum number in the interval [start, end] by the given root of segment tree. Notice It is much easier to understand this problem if you finished Segment Tree Build first. Have you met this question in a real interview? Yes Example For array [1, 4, 2, 3], the corresponding Segment Tree is: [0, 3, max=4] / \ [0,1,max=4] [2,3,max=3] / \ / \ [0,0,max=1] [1,1,max=4] [2,2,max=2], [3,3,max=3] query(root, 1, 1), return 4 query(root, 1, 2), return 4 query(root, 2, 3), return 3 query(root, 0, 2), return 4
思路:查询给定区间中的最大值,根据给定区间与当前区间节点的三种位置关系来执行相应的操作,注意要包含给定区间超过原有区间范围的情况。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition of SegmentTreeNode: 3 * public class SegmentTreeNode { 4 * public int start, end, max; 5 * public SegmentTreeNode left, right; 6 * public SegmentTreeNode(int start, int end, int max) { 7 * this.start = start; 8 * this.end = end; 9 * this.max = max 10 * this.left = this.right = null; 11 * } 12 * } 13 */ 14 public class Solution { 15 /** 16 *@param root, start, end: The root of segment tree and 17 * an segment / interval 18 *@return: The maximum number in the interval [start, end] 19 */ 20 public int query(SegmentTreeNode root, int start, int end) { 21 // write your code here 22 if (root == null || start > end) { 23 return 0; 24 } 25 if (root.start >= start && root.end <= end) { 26 return root.max; 27 } 28 int mid = root.start + (root.end - root.start) / 2; 29 if (start <= mid && end <= mid) { 30 return query(root.left, start, end); 31 } 32 if (start <= mid && end > mid) { 33 return Math.max(query(root.left, start, mid), 34 query(root.right, mid + 1, end)); 35 } 36 return query(root.right, start, end); 37 } 38 }
Segment Tree Query II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
For an array, we can build a SegmentTree for it, each node stores an extra attribute count to denote the number of elements in the the array which value is between interval start and end. (The array may not fully filled by elements) Design a query method with three parameters root, start and end, find the number of elements in the in array's interval [start, end] by the given root of value SegmentTree. Notice It is much easier to understand this problem if you finished Segment Tree Buildand Segment Tree Query first. Have you met this question in a real interview? Yes Example For array [0, 2, 3], the corresponding value Segment Tree is: [0, 3, count=3] / \ [0,1,count=1] [2,3,count=2] / \ / \ [0,0,count=1] [1,1,count=0] [2,2,count=1], [3,3,count=1] query(1, 1), return 0 query(1, 2), return 1 query(2, 3), return 2 query(0, 2), return 2
思路:查询给定区间中的元素个数,根据给定区间与当前区间节点的三种位置关系来执行相应的操作,注意要包含给定区间超过原有区间范围的情况。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition of SegmentTreeNode: 3 * public class SegmentTreeNode { 4 * public int start, end, count; 5 * public SegmentTreeNode left, right; 6 * public SegmentTreeNode(int start, int end, int count) { 7 * this.start = start; 8 * this.end = end; 9 * this.count = count; 10 * this.left = this.right = null; 11 * } 12 * } 13 */ 14 public class Solution { 15 /** 16 *@param root, start, end: The root of segment tree and 17 * an segment / interval 18 *@return: The count number in the interval [start, end] 19 */ 20 public int query(SegmentTreeNode root, int start, int end) { 21 // write your code here 22 if (root == null || start > end) { 23 return 0; 24 } 25 if (root.start >= start && root.end <= end) { 26 return root.count; 27 } 28 int mid = root.start + (root.end - root.start) / 2; 29 if (start <= mid && end <= mid) { 30 return query(root.left, start, end); 31 } 32 if (start <= mid && end > mid) { 33 return query(root.left, start, mid) 34 + query(root.right, mid + 1, end); 35 } 36 return query(root.right, start, end); 37 } 38 }
Segment Tree Modify
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
For a Maximum Segment Tree, which each node has an extra value max to store the maximum value in this node's interval. Implement a modify function with three parameter root, index and value to change the node's value with [start, end] = [index, index] to the new given value. Make sure after this change, every node in segment tree still has the max attribute with the correct value. Notice We suggest you finish problem Segment Tree Build and Segment Tree Query first. Have you met this question in a real interview? Yes Example For segment tree: [1, 4, max=3] / \ [1, 2, max=2] [3, 4, max=3] / \ / \ [1, 1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=3] if call modify(root, 2, 4), we can get: [1, 4, max=4] / \ [1, 2, max=4] [3, 4, max=3] / \ / \ [1, 1, max=2], [2, 2, max=4], [3, 3, max=0], [4, 4, max=3] or call modify(root, 4, 0), we can get: [1, 4, max=2] / \ [1, 2, max=2] [3, 4, max=0] / \ / \ [1, 1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=0]
思路:改变某一元素的值从而更新线段树。根据要改变值的索引index与当前区间的中点mid的大小关系来选择相应的操作,记得从小到大更新遍历过的区间的max属性。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition of SegmentTreeNode: 3 * public class SegmentTreeNode { 4 * public int start, end, max; 5 * public SegmentTreeNode left, right; 6 * public SegmentTreeNode(int start, int end, int max) { 7 * this.start = start; 8 * this.end = end; 9 * this.max = max 10 * this.left = this.right = null; 11 * } 12 * } 13 */ 14 public class Solution { 15 /** 16 *@param root, index, value: The root of segment tree and 17 *@ change the node's value with [index, index] to the new given value 18 *@return: void 19 */ 20 public void modify(SegmentTreeNode root, int index, int value) { 21 // write your code here 22 if (root == null || index < root.start || index > root.end) { 23 return; 24 } 25 if (root.start == root.end) { 26 root.max = value; 27 return; 28 } 29 int mid = root.start + (root.end - root.start) / 2; 30 if (index <= mid) { 31 modify(root.left, index, value); 32 } 33 if (index > mid) { 34 modify(root.right, index, value); 35 } 36 root.max = Math.max(root.left.max, root.right.max); 37 } 38 }
Interval Sum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an integer array (index from 0 to n-1, where n is the size of this array), and an query list. Each query has two integers [start, end]. For each query, calculate the sum number between index start and end in the given array, return the result list. Notice We suggest you finish problem Segment Tree Build, Segment Tree Query and Segment Tree Modify first. Have you met this question in a real interview? Yes Example For array [1,2,7,8,5], and queries [(0,4),(1,2),(2,4)], return [23,9,20]
思路:线段树build + query的结合,注意query函数中是start ~ end与当前线段树节点的root.start ~ root.end相比,所以query中mid = root.start + (root.end - root.start) / 2,是当前线段树节点的区间中点,不要误写成start + (end - start) / 2,错弄成待查询的区间中点,这个错误非常容易犯。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition of Interval: 3 * public classs Interval { 4 * int start, end; 5 * Interval(int start, int end) { 6 * this.start = start; 7 * this.end = end; 8 * } 9 */ 10 class SegmentTreeNode { 11 int start; 12 int end; 13 long sum; 14 SegmentTreeNode left; 15 SegmentTreeNode right; 16 public SegmentTreeNode(int start, int end, long sum) { 17 this.start = start; 18 this.end = end; 19 this.sum = sum; 20 left = null; 21 right = null; 22 } 23 } 24 public class Solution { 25 /** 26 *@param A, queries: Given an integer array and an query list 27 *@return: The result list 28 */ 29 public ArrayList<Long> intervalSum(int[] A, 30 ArrayList<Interval> queries) { 31 // write your code here 32 ArrayList<Long> result = new ArrayList<>(); 33 if (A == null || A.length == 0 34 || queries == null || queries.size() == 0) { 35 return result; 36 } 37 SegmentTreeNode root = build(A, 0, A.length - 1); 38 for (Interval interval : queries) { 39 result.add(query(root, interval.start, interval.end)); 40 } 41 return result; 42 } 43 public SegmentTreeNode build(int[] A, int start, int end) { 44 if (start == end) { 45 return new SegmentTreeNode(start, end, A[start]); 46 } 47 SegmentTreeNode root = new SegmentTreeNode(start, end, 0); 48 int mid = start + (end - start) / 2; 49 root.left = build(A, start, mid); 50 root.right = build(A, mid + 1, end); 51 root.sum = root.left.sum + root.right.sum; 52 return root; 53 } 54 public long query(SegmentTreeNode root, int start, int end) { 55 if (root == null || start > end) { 56 return 0; 57 } 58 if (start <= root.start && end >= root.end) { 59 return root.sum; 60 } 61 int mid = root.start + (root.end - root.start) / 2; 62 if (start <= mid && end <= mid) { 63 return query(root.left, start, end); 64 } 65 if (start <= mid && end > mid) { 66 return query(root.left, start, mid) 67 + query(root.right, mid + 1, end); 68 } 69 return query(root.right, start, end); 70 } 71 }
Interval Sum II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an integer array in the construct method, implement two methods query(start, end) and modify(index, value): For query(start, end), return the sum from index start to index end in the given array. For modify(index, value), modify the number in the given index to value Notice We suggest you finish problem Segment Tree Build, Segment Tree Query and Segment Tree Modify first. Have you met this question in a real interview? Yes Example Given array A = [1,2,7,8,5]. query(0, 2), return 10. modify(0, 4), change A[0] from 1 to 4. query(0, 1), return 6. modify(2, 1), change A[2] from 7 to 1. query(2, 4), return 14.
思路:线段树build + modify + query的结合。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class SegmentTreeNode { 2 int start; 3 int end; 4 long sum; 5 SegmentTreeNode left; 6 SegmentTreeNode right; 7 public SegmentTreeNode(int start, int end, long sum) { 8 this.start = start; 9 this.end = end; 10 this.sum = sum; 11 left = null; 12 right = null; 13 } 14 } 15 public class Solution { 16 /* you may need to use some attributes here */ 17 SegmentTreeNode root; 18 /** 19 * @param A: An integer array 20 */ 21 public Solution(int[] A) { 22 // write your code here 23 root = build(A, 0, A.length - 1); 24 } 25 26 public SegmentTreeNode build(int[] num, int start, int end) { 27 if (num == null || num.length == 0 || start > end) { 28 return null; 29 } 30 if (start == end) { 31 return new SegmentTreeNode(start, start, num[start]); 32 } 33 SegmentTreeNode root = new SegmentTreeNode(start, end, 0); 34 int mid = start + (end - start) / 2; 35 root.left = build(num, start, mid); 36 root.right = build(num, mid + 1, end); 37 root.sum = root.left.sum + root.right.sum; 38 return root; 39 } 40 41 /** 42 * @param start, end: Indices 43 * @return: The sum from start to end 44 */ 45 public long query(int start, int end) { 46 // write your code here 47 return query(root, start, end); 48 } 49 50 public long query(SegmentTreeNode root, int start, int end) { 51 if (root == null || start > end) { 52 return 0; 53 } 54 if (start <= root.start && end >= root.end) { 55 return root.sum; 56 } 57 int mid = root.start + (root.end - root.start) / 2; 58 if (start <= mid && end <= mid) { 59 return query(root.left, start, end); 60 } 61 if (start <= mid && end > mid) { 62 return query(root.left, start, mid) 63 + query(root.right, mid + 1, end); 64 } 65 return query(root.right, start, end); 66 } 67 68 /** 69 * @param index, value: modify A[index] to value. 70 */ 71 public void modify(int index, int value) { 72 // write your code here 73 modify(root, index, value); 74 } 75 76 public void modify(SegmentTreeNode root, int index, int value) { 77 if (root.start == root.end) { 78 root.sum = value; 79 return; 80 } 81 int mid = root.start + (root.end - root.start) / 2; 82 if (index <= mid) { 83 modify(root.left, index, value); 84 } else { 85 modify(root.right, index, value); 86 } 87 root.sum = root.left.sum + root.right.sum; 88 } 89 }
Interval Minimum Number
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an integer array (index from 0 to n-1, where n is the size of this array), and an query list. Each query has two integers [start, end]. For each query, calculate the minimum number between index start and end in the given array, return the result list. Notice We suggest you finish problem Segment Tree Build, Segment Tree Query and Segment Tree Modify first. Have you met this question in a real interview? Yes Example For array [1,2,7,8,5], and queries [(1,2),(0,4),(2,4)], return [2,1,5]
思路:线段树build + query结合,只要熟悉线段树的模版,写起来很容易。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition of Interval: 3 * public classs Interval { 4 * int start, end; 5 * Interval(int start, int end) { 6 * this.start = start; 7 * this.end = end; 8 * } 9 */ 10 class SegmentTreeNode { 11 int start; 12 int end; 13 int min; 14 SegmentTreeNode left; 15 SegmentTreeNode right; 16 public SegmentTreeNode(int start, int end, int min) { 17 this.start = start; 18 this.end = end; 19 this.min = min; 20 left = null; 21 right = null; 22 } 23 } 24 public class Solution { 25 /** 26 *@param A, queries: Given an integer array and an query list 27 *@return: The result list 28 */ 29 public ArrayList<Integer> intervalMinNumber(int[] A, 30 ArrayList<Interval> queries) { 31 // write your code here 32 ArrayList<Integer> result = new ArrayList<>(); 33 if (A == null || A.length == 0 34 || queries == null || queries.size() == 0) { 35 return result; 36 } 37 SegmentTreeNode root = build(A, 0, A.length - 1); 38 for (Interval interval : queries) { 39 result.add(query(root, interval.start, interval.end)); 40 } 41 return result; 42 } 43 public SegmentTreeNode build(int[] A, int start, int end) { 44 if (A == null || A.length == 0 || start > end) { 45 return null; 46 } 47 if (start == end) { 48 return new SegmentTreeNode(start, start, A[start]); 49 } 50 SegmentTreeNode root = new SegmentTreeNode(start, end, 0); 51 int mid = start + (end - start) / 2; 52 root.left = build(A, start, mid); 53 root.right = build(A, mid + 1, end); 54 root.min = Math.min(root.left.min, root.right.min); 55 return root; 56 } 57 public int query(SegmentTreeNode root, int start, int end) { 58 if (root == null || start > end) { 59 return 0; 60 } 61 if (start <= root.start && end >= root.end) { 62 return root.min; 63 } 64 int mid = root.start + (root.end - root.start) / 2; 65 if (start <= mid && end <= mid) { 66 return query(root.left, start, end); 67 } 68 if (start <= mid && end > mid) { 69 return Math.min(query(root.left, start, mid), 70 query(root.right, mid + 1, end)); 71 } 72 return query(root.right, start, end); 73 } 74 }
Count of Smaller Number
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Give you an integer array (index from 0 to n-1, where n is the size of this array, value from 0 to 10000) and an query list. For each query, give you an integer, return the number of element in the array that are smaller than the given integer. Notice We suggest you finish problem Segment Tree Build and Segment Tree Query II first. Have you met this question in a real interview? Yes Example For array [1,2,7,8,5], and queries [1,8,5], return [0,4,2]
思路I:数组中最小值min,最大值max,map存储每个元素出现的次数,以这三个来构建线段树。注意这一题的边界情况,queries数组len >= 1时,对每一个询问必须有返回值(0或者非0),当A数组为空或者len == 0时不能直接返回空的list,而是返回长度为queries.length,元素都为0的list。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class SegmentTreeNode { 2 int start; 3 int end; 4 int count; 5 SegmentTreeNode left; 6 SegmentTreeNode right; 7 public SegmentTreeNode(int start, int end, int count) { 8 this.start = start; 9 this.end = end; 10 this.count = count; 11 left = null; 12 right = null; 13 } 14 } 15 public class Solution { 16 /** 17 * @param A: An integer array 18 * @return: The number of element in the array that 19 * are smaller that the given integer 20 */ 21 public ArrayList<Integer> countOfSmallerNumber(int[] A, int[] queries) { 22 // write your code here 23 ArrayList<Integer> result = new ArrayList<>(); 24 if (queries == null || queries.length == 0) { 25 return result; 26 } 27 if (A == null || A.length == 0) { 28 for (int i = 0; i < queries.length; i++) { 29 result.add(0); 30 } 31 return result; 32 } 33 int min = Integer.MAX_VALUE; 34 int max = Integer.MIN_VALUE; 35 Map<Integer, Integer> map = new HashMap<>(); 36 for (int i = 0; i < A.length; i++) { 37 min = Math.min(min, A[i]); 38 max = Math.max(max, A[i]); 39 if (!map.containsKey(A[i])) { 40 map.put(A[i], 0); 41 } 42 map.put(A[i], map.get(A[i]) + 1); 43 } 44 SegmentTreeNode root = build(min, max, map); 45 for (int value : queries) { 46 result.add(query(root, value - 1)); 47 } 48 return result; 49 } 50 public SegmentTreeNode build(int start, int end, 51 Map<Integer, Integer> map) { 52 if (start > end || map == null || map.size() == 0) { 53 return null; 54 } 55 //System.out.println("empty"); 56 if (start == end) { 57 if (!map.containsKey(start)) { 58 return new SegmentTreeNode(start, start, 0); 59 } 60 return new SegmentTreeNode(start, start, map.get(start)); 61 } 62 SegmentTreeNode root = new SegmentTreeNode(start, end, 0); 63 int mid = start + (end - start) / 2; 64 root.left = build(start, mid, map); 65 root.right = build(mid + 1, end, map); 66 root.count = root.left.count + root.right.count; 67 return root; 68 } 69 public int query(SegmentTreeNode root, int value) { 70 if (root == null) { 71 return 0; 72 } 73 if (value >= root.end) { 74 return root.count; 75 } 76 if (value < root.start) { 77 return 0; 78 } 79 int mid = root.start + (root.end - root.start) / 2; 80 if (value <= mid) { 81 return query(root.left, value); 82 } 83 return root.left.count + query(root.right, value); 84 } 85 }
思路II:考虑可扩展性,同Count of Smaller Number before itself。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class SegmentTreeNode { 2 int start; 3 int end; 4 int count; 5 SegmentTreeNode left; 6 SegmentTreeNode right; 7 public SegmentTreeNode(int start, int end, int count) { 8 this.start = start; 9 this.end = end; 10 this.count = count; 11 left = null; 12 right = null; 13 } 14 } 15 public class Solution { 16 /** 17 * @param A: An integer array 18 * @return: The number of element in the array that 19 * are smaller that the given integer 20 */ 21 public ArrayList<Integer> countOfSmallerNumber(int[] A, int[] queries) { 22 // write your code here 23 ArrayList<Integer> result = new ArrayList<>(); 24 if (queries == null || queries.length == 0) { 25 return result; 26 } 27 if (A == null || A.length == 0) { 28 for (int i = 0; i < queries.length; i++) { 29 result.add(0); 30 } 31 return result; 32 } 33 SegmentTreeNode root = build(0, 10000); 34 for (int num : A) { 35 modify(root, num, 1); 36 } 37 for (int num : queries) { 38 result.add(query(root, 0, num - 1)); 39 } 40 return result; 41 } 42 public SegmentTreeNode build(int start, int end) { 43 if (start > end) { 44 return null; 45 } 46 if (start == end) { 47 return new SegmentTreeNode(start, start, 0); 48 } 49 SegmentTreeNode root = new SegmentTreeNode(start, end, 0); 50 int mid = start + (end - start) / 2; 51 root.left = build(start, mid); 52 root.right = build(mid + 1, end); 53 return root; 54 } 55 public int query(SegmentTreeNode root, int start, int end) { 56 if (root == null || start > end) { 57 return 0; 58 } 59 if (start <= root.start && end >= root.end) { 60 return root.count; 61 } 62 int mid = root.start + (root.end - root.start) / 2; 63 if (start <= mid && end <= mid) { 64 return query(root.left, start, end); 65 } 66 if (start <= mid && end > mid) { 67 return query(root.left, start, mid) 68 + query(root.right, mid + 1, end); 69 } 70 return query(root.right, start, end); 71 } 72 public void modify(SegmentTreeNode root, int index, int value) { 73 if (root == null) { 74 return; 75 } 76 if (root.start == root.end) { 77 root.count += value; 78 return; 79 } 80 int mid = root.start + (root.end - root.start) / 2; 81 if (index <= mid) { 82 modify(root.left, index, value); 83 } else { 84 modify(root.right, index, value); 85 } 86 root.count = root.left.count + root.right.count; 87 } 88 }
Count of Smaller Number before Itself
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Give you an integer array (index from 0 to n-1, where n is the size of this array, data value from 0 to 10000) . For each element Ai in the array, count the number of element before this element Ai is smaller than it and return count number array. Notice We suggest you finish problem Segment Tree Build, Segment Tree Query II and Count of Smaller Number first. Have you met this question in a real interview? Yes Example For array [1,2,7,8,5], return [0,1,2,3,2]
思路:将数组A中的元素作为索引,对应的值为该元素个数,建立线段树。每次遍历A中的元素时,先在线段树中query,再modify,更新线段树中节点的count值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class SegmentTreeNode { 2 int start; 3 int end; 4 int count; 5 SegmentTreeNode left; 6 SegmentTreeNode right; 7 public SegmentTreeNode(int start, int end, int count) { 8 this.start = start; 9 this.end = end; 10 this.count = count; 11 left = null; 12 right = null; 13 } 14 } 15 public class Solution { 16 /** 17 * @param A: An integer array 18 * @return: Count the number of element before this element 'ai' is 19 * smaller than it and return count number array 20 */ 21 public ArrayList<Integer> countOfSmallerNumberII(int[] A) { 22 // write your code here 23 ArrayList<Integer> result = new ArrayList<>(); 24 if (A == null || A.length == 0) { 25 return result; 26 } 27 SegmentTreeNode root = build(0, 10000); 28 for (int num : A) { 29 result.add(query(root, 0, num - 1)); 30 modify(root, num, 1); 31 } 32 return result; 33 } 34 public SegmentTreeNode build(int start, int end) { 35 if (start > end) { 36 return null; 37 } 38 if (start == end) { 39 return new SegmentTreeNode(start, start, 0); 40 } 41 SegmentTreeNode root = new SegmentTreeNode(start, end, 0); 42 int mid = start + (end - start) / 2; 43 root.left = build(start, mid); 44 root.right = build(mid + 1, end); 45 return root; 46 } 47 public int query(SegmentTreeNode root, int start, int end) { 48 if (root == null || start > end) { 49 return 0; 50 } 51 if (start <= root.start && end >= root.end) { 52 return root.count; 53 } 54 int mid = root.start + (root.end - root.start) / 2; 55 if (start <= mid && end <= mid) { 56 return query(root.left, start, end); 57 } 58 if (start <= mid && end > mid) { 59 return query(root.left, start, mid) 60 + query(root.right, mid + 1, end); 61 } 62 return query(root.right, start, end); 63 } 64 public void modify(SegmentTreeNode root, int index, int value) { 65 if (root == null) { 66 return; 67 } 68 if (root.start == root.end) { 69 root.count += value; 70 return; 71 } 72 int mid = root.start + (root.end - root.start) / 2; 73 if (index <= mid) { 74 modify(root.left, index, value); 75 } else { 76 modify(root.right, index, value); 77 } 78 root.count = root.left.count + root.right.count; 79 } 80 }
Min Stack
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Implement a stack with min() function, which will return the smallest number in the stack. It should support push, pop and min operation all in O(1) cost. Notice min operation will never be called if there is no number in the stack. Have you met this question in a real interview? Yes Example push(1) pop() // return 1 push(2) push(3) min() // return 2 push(1) min() // return 1
思路I:两个栈,一个栈stack保存元素,另一个栈minStack保存当前stack中的最小值,minStack元素个数等于stack中的元素个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class MinStack { 2 Stack<Integer> stack; 3 Stack<Integer> minStack; 4 public MinStack() { 5 // do initialize if necessary 6 stack = new Stack<Integer>(); 7 minStack = new Stack<Integer>(); 8 } 9 10 public void push(int number) { 11 // write your code here 12 stack.push(number); 13 if (minStack.isEmpty()) { 14 minStack.push(number); 15 } else { 16 minStack.push(Math.min(number, minStack.peek())); 17 } 18 } 19 20 public int pop() { 21 // write your code here 22 minStack.pop(); 23 return stack.pop(); 24 } 25 26 public int min() { 27 // write your code here 28 return minStack.peek(); 29 } 30 }
优化:minStack只存储小于等于当前minStack栈顶元素的值。但是空间复杂度不会改变。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class MinStack { 2 Stack<Integer> stack; 3 Stack<Integer> minStack; 4 public MinStack() { 5 // do initialize if necessary 6 stack = new Stack<Integer>(); 7 minStack = new Stack<Integer>(); 8 } 9 10 public void push(int number) { 11 // write your code here 12 stack.push(number); 13 if (minStack.isEmpty()) { 14 minStack.push(number); 15 } else { 16 if (number <= minStack.peek()) { 17 minStack.push(number); 18 } 19 } 20 } 21 22 public int pop() { 23 // write your code here 24 //注意这里判断两个Integer对象相等时一律用equals方法,直邮Integer的值在-128 - 127才不会new Integer,直接==比较不会出错。超出这个范围比较的就是内存地址。 25 if (stack.peek().equals(minStack.peek())) { 26 minStack.pop(); 27 } 28 return stack.pop(); 29 } 30 31 public int min() { 32 // write your code here 33 return minStack.peek(); 34 } 35 }
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. histogram Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3]. histogram The largest rectangle is shown in the shaded area, which has area = 10 unit. Have you met this question in a real interview? Yes Example Given height = [2,1,5,6,2,3], return 10.
思路:单调栈 注意循环次数为length + 1次!!!栈中存储的是索引。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param height: A list of integer 4 * @return: The area of largest rectangle in the histogram 5 */ 6 public int largestRectangleArea(int[] height) { 7 // write your code here 8 if (height == null || height.length == 0) { 9 return 0; 10 } 11 Stack<Integer> stack = new Stack<>(); 12 int max = 0; 13 // 注意这里的i要循环到height.length 14 for (int i = 0; i <= height.length; i++) { 15 int cur = i == height.length ? 0 : height[i]; 16 while (!stack.isEmpty() && cur <= height[stack.peek()]) { 17 int h = height[stack.pop()]; 18 int w = stack.isEmpty() ? i : i - stack.peek() - 1; 19 max = Math.max(max, h * w); 20 } 21 stack.push(i); 22 } 23 return max; 24 } 25 }
Maximal Rectangle
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a 2D boolean matrix filled with False and True, find the largest rectangle containing all True and return its area. Have you met this question in a real interview? Yes Example Given a matrix: [ [1, 1, 0, 0, 1], [0, 1, 0, 0, 1], [0, 0, 1, 1, 1], [0, 0, 1, 1, 1], [0, 0, 0, 0, 1] ] return 6.
思路:单调栈 二维数组从上到下累计直方图的高度,每一行看作是直方图的宽度。然后转化为求每一行的直方图能构成的最大面积即转化成Largest Rectangle in Histogram问题。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param matrix a boolean 2D matrix 4 * @return an integer 5 */ 6 public int maximalRectangle(boolean[][] matrix) { 7 // Write your code here 8 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 9 return 0; 10 } 11 int n = matrix.length; 12 int m = matrix[0].length; 13 int[][] height = new int[n][m]; 14 for (int i = 0; i < m; i++) { 15 height[0][i] = matrix[0][i] ? 1 : 0; 16 } 17 for (int i = 1; i < n; i++) { 18 for (int j = 0; j < m; j++) { 19 height[i][j] = matrix[i][j] ? height[i - 1][j] + 1 : 0; 20 } 21 } 22 int result = 0; 23 for (int i = 0; i < n; i++) { 24 Stack<Integer> stack = new Stack<>(); 25 for (int j = 0; j <= m; j++) { 26 int cur = j == m ? 0 : height[i][j]; 27 while (!stack.isEmpty() && cur <= height[i][stack.peek()]) { 28 int h = height[i][stack.pop()]; 29 int w = stack.isEmpty() ? j : j - stack.peek() - 1; 30 result = Math.max(result, h * w); 31 } 32 stack.push(j); 33 } 34 } 35 return result; 36 } 37 }
Max Tree
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an integer array with no duplicates. A max tree building on this array is defined as follow: The root is the maximum number in the array The left subtree and right subtree are the max trees of the subarray divided by the root number. Construct the max tree by the given array. Have you met this question in a real interview? Yes Example Given [2, 5, 6, 0, 3, 1], the max tree constructed by this array is: 6 / \ 5 3 / / \ 2 0 1
思路I:分治法 但是时间复杂度最坏可达到O(n^2),栈深过大会溢出,如升序序列。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition of TreeNode: 3 * public class TreeNode { 4 * public int val; 5 * public TreeNode left, right; 6 * public TreeNode(int val) { 7 * this.val = val; 8 * this.left = this.right = null; 9 * } 10 * } 11 */ 12 class Point { 13 int index; 14 int val; 15 public Point(int index, int val) { 16 this.index = index; 17 this.val = val; 18 } 19 } 20 public class Solution { 21 /** 22 * @param A: Given an integer array with no duplicates. 23 * @return: The root of max tree. 24 */ 25 public TreeNode maxTree(int[] A) { 26 // write your code here 27 if (A == null || A.length == 0) { 28 return null; 29 } 30 return dfsHelper(A, 0, A.length - 1); 31 } 32 public TreeNode dfsHelper(int[] A, int start, int end) { 33 if (start > end) { 34 return null; 35 } 36 if (start == end) { 37 return new TreeNode(A[start]); 38 } 39 Point point = findMaximum(A, start, end); 40 //System.out.println(point.index + " " + point.val); 41 TreeNode root = new TreeNode(point.val); 42 root.left = dfsHelper(A, start, point.index - 1); 43 root.right = dfsHelper(A, point.index + 1, end); 44 return root; 45 } 46 public Point findMaximum(int[] A, int start, int end) { 47 int index = -1; 48 int max = Integer.MIN_VALUE; 49 for (int i = start; i <= end; i++) { 50 if (A[i] >= max) { 51 max = A[i]; 52 index = i; 53 } 54 } 55 return new Point(index, max); 56 } 57 }
思路II:单调递减栈,找出每个数往左数第一个比他大的数和往右数第一个比他大的数,两者中较小的数即为该数的父亲节点。每个数往左数第一个比他大的数用单调递减栈来维护,当遇到往右数第一个比他大的数就把栈顶元素pop出来,补充该节点与父亲节点的关系。循环次数为length(数组长度) + 1。为了完善所有节点的关系,虚拟出一个节点,值为Integer.MAX_VALUE,确保所有节点的关系。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition of TreeNode: 3 * public class TreeNode { 4 * public int val; 5 * public TreeNode left, right; 6 * public TreeNode(int val) { 7 * this.val = val; 8 * this.left = this.right = null; 9 * } 10 * } 11 */ 12 public class Solution { 13 /** 14 * @param A: Given an integer array with no duplicates. 15 * @return: The root of max tree. 16 */ 17 public TreeNode maxTree(int[] A) { 18 // write your code here 19 if (A == null || A.length == 0) { 20 return null; 21 } 22 Stack<TreeNode> stack = new Stack<>(); 23 for (int i = 0; i <= A.length; i++) { 24 TreeNode right = i == A.length ? new TreeNode(Integer.MAX_VALUE) : new TreeNode(A[i]); 25 while (!stack.isEmpty() && right.val >= stack.peek().val) { 26 TreeNode cur = stack.pop(); 27 if (stack.isEmpty()) { 28 right.left = cur; 29 } else { 30 TreeNode left = stack.peek(); 31 if (left.val > right.val) { 32 right.left = cur; 33 } else { 34 left.right = cur; 35 } 36 } 37 } 38 stack.push(right); 39 } 40 return stack.peek().left; 41 } 42 }
Expression Tree Build
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
The structure of Expression Tree is a binary tree to evaluate certain expressions. All leaves of the Expression Tree have an number string value. All non-leaves of the Expression Tree have an operator string value. Now, given an expression array, build the expression tree of this expression, return the root of this expression tree. Have you met this question in a real interview? Yes Clarification See wiki: Expression Tree Example For the expression (2*6-(23+7)/(1+2)) (which can be represented by ["2" "*" "6" "-" "(" "23" "+" "7" ")" "/" "(" "1" "+" "2" ")"]). The expression tree will be like [ - ] / \ [ * ] [ / ] / \ / \ [ 2 ] [ 6 ] [ + ] [ + ] / \ / \ [ 23 ][ 7 ] [ 1 ] [ 2 ] . After building the tree, you just need to return root node [-].
思路:首先定义一个Node
,对于每一个符号或者数字,附上一个val,以便于之后evaluate它,来建立树
如果expression[i]
等于"("或者")"调整base然后continue
跳过后面的步骤
维护一个单调递增stack,建立最小树,类似题[Max Tree]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition of ExpressionTreeNode: 3 * public class ExpressionTreeNode { 4 * public String symbol; 5 * public ExpressionTreeNode left, right; 6 * public ExpressionTreeNode(String symbol) { 7 * this.symbol = symbol; 8 * this.left = this.right = null; 9 * } 10 * } 11 */ 12 class Node { 13 int val; 14 ExpressionTreeNode treeNode; 15 public Node(int val, String s) { 16 this.val = val; 17 treeNode = new ExpressionTreeNode(s); 18 } 19 } 20 public class Solution { 21 /** 22 * @param expression: A string array 23 * @return: The root of expression tree 24 */ 25 public ExpressionTreeNode build(String[] expression) { 26 // write your code here 27 if (expression == null || expression.length == 0) { 28 return null; 29 } 30 Stack<Node> stack = new Stack<>(); 31 int base = 0; 32 int val = 0; 33 for (int i = 0; i <= expression.length; i++) { 34 if (i < expression.length) { 35 if (expression[i].equals("(")) { 36 base += 10; 37 continue; 38 } 39 if (expression[i].equals(")")) { 40 base -= 10; 41 continue; 42 } 43 val = getWeight(base, expression[i]); 44 } 45 Node right = i == expression.length ? 46 new Node(Integer.MIN_VALUE, "") : new Node(val, expression[i]); 47 while (!stack.isEmpty() && right.val <= stack.peek().val) { 48 Node cur = stack.pop(); 49 if (stack.isEmpty()) { 50 right.treeNode.left = cur.treeNode; 51 } else { 52 if (stack.peek().val < right.val) { 53 right.treeNode.left = cur.treeNode; 54 } else { 55 stack.peek().treeNode.right = cur.treeNode; 56 } 57 } 58 } 59 stack.push(right); 60 } 61 return stack.pop().treeNode.left; 62 } 63 public int getWeight(int base, String s) { 64 if (s.equals("+") || s.equals("-")) { 65 return base + 1; 66 } 67 if (s.equals("*") || s.equals("/")) { 68 return base + 2; 69 } 70 return Integer.MAX_VALUE; 71 } 72 }
Expression Evaluation
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an expression string array, return the final result of this expression Notice The expression contains only integer, +, -, *, /, (, ). Have you met this question in a real interview? Yes Example For the expression 2*6-(23+7)/(1+2), input is [ "2", "*", "6", "-", "(", "23", "+", "7", ")", "/", (", "1", "+", "2", ")" ], return 2
思路:两个栈,一个栈存数字,一个栈存操作符。
入栈操作:
1. 遇到"(",放入操作符栈
2. 遇到")",取出数字栈中两个数与操作符栈中一个操作符运算,将结果存入数字栈,循环该过程直到遇到"(",然后从操作符栈中pop出"("。
3. 遇到操作符,如果当前操作符栈中的优先级等于或者高于(等于也要运算,防止类似"999/3/3/3"表达式执行错误的运算顺序)该操作符,取出数字栈中两 个数与操作符栈中一个操作符运算,将结果存入数字栈,循环该过程,直到当前操作符栈中的优先级小于该操作符或者遇到"("。
4. 遇到数字,直接存入数字栈。
出栈操作:
然后对数字栈和操作符栈中剩余的元素进行:取出数字栈中两个数与操作符栈中一个操作符运算,将结果存入数字栈,循环该过程。最后操作符栈应该为空, 数字栈只剩下一个数,返回这个数。注意这里需要判断数字栈是否为空,比如表达式"((()))"执行程序后数字栈为空,返回0;不为空才返回这个数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param expression: an array of strings; 4 * @return: an integer 5 */ 6 public int evaluateExpression(String[] expression) { 7 // write your code here 8 if (expression == null || expression.length == 0) { 9 return 0; 10 } 11 Stack<Integer> values = new Stack<>(); 12 Stack<String> ops = new Stack<>(); 13 for (int i = 0; i < expression.length; i++) { 14 if (expression[i].equals("(")) { 15 ops.push("("); 16 } else if (expression[i].equals(")")) { 17 while (!ops.peek().equals("(")) { 18 int result = calculate(values, ops); 19 values.push(result); 20 } 21 ops.pop(); 22 } else if (isOperation(expression[i])) { 23 while (!ops.isEmpty() 24 && hasPrecedence(ops.peek(), expression[i])) { 25 int result = calculate(values, ops); 26 values.push(result); 27 } 28 ops.push(expression[i]); 29 } else { 30 values.push(Integer.parseInt(expression[i])); 31 } 32 } 33 while (!ops.isEmpty()) { 34 int result = calculate(values, ops); 35 values.push(result); 36 } 37 if (values.isEmpty()) { 38 return 0; 39 } 40 return values.pop(); 41 } 42 public boolean isOperation(String s) { 43 return s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/"); 44 } 45 public int calculate(Stack<Integer> values, Stack<String> ops) { 46 String operation = ops.pop(); 47 int a = values.pop(); 48 int b = values.pop(); 49 if (operation.equals("+")) { 50 return b + a; 51 } else if (operation.equals("-")) { 52 return b - a; 53 } else if (operation.equals("*")) { 54 return b * a; 55 } else { 56 return b / a; 57 } 58 } 59 public boolean hasPrecedence(String s1, String s2) { 60 if (s1.equals("(")) { 61 return false; 62 } 63 if ((s1.equals("+") || s1.equals("-")) 64 && (s2.equals("*") || s2.equals("/"))) { 65 return false; 66 } 67 return true; 68 } 69 }
Convert Expression to Reverse Polish Notation
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an expression string array, return the Reverse Polish notation of this expression. (remove the parentheses) Have you met this question in a real interview? Yes Example For the expression [3 - 4 + 5] (which denote by ["3", "-", "4", "+", "5"]), return [3 4 - 5 +] (which denote by ["3", "4", "-", "5", "+"])
思路:先Expression Tree Build,然后后序遍历该树就能求出逆波兰表示法。逆波兰表示法实际上就是二叉树的后序遍历。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class TreeNode { 2 int val; 3 String s; 4 TreeNode left; 5 TreeNode right; 6 public TreeNode(int val, String s) { 7 this.val = val; 8 this.s = s; 9 left = null; 10 right = null; 11 } 12 } 13 public class Solution { 14 /** 15 * @param expression: A string array 16 * @return: The Reverse Polish notation of this expression 17 */ 18 public ArrayList<String> convertToRPN(String[] expression) { 19 // write your code here 20 ArrayList<String> result = new ArrayList<>(); 21 if (expression == null || expression.length == 0) { 22 return result; 23 } 24 Stack<TreeNode> stack = new Stack<>(); 25 int val = 0; 26 int base = 0; 27 for (int i = 0; i <= expression.length; i++) { 28 if (i < expression.length) { 29 if (expression[i].equals("(")) { 30 base += 10; 31 continue; 32 } 33 if (expression[i].equals(")")) { 34 base -= 10; 35 continue; 36 } 37 val = getWeight(base, expression[i]); 38 } 39 TreeNode right = i == expression.length ? new TreeNode(Integer.MIN_VALUE, "") : new TreeNode(val, expression[i]); 40 while (!stack.isEmpty() && right.val <= stack.peek().val) { 41 TreeNode cur = stack.pop(); 42 if (stack.isEmpty()) { 43 right.left = cur; 44 } else { 45 if (stack.peek().val < right.val) { 46 right.left = cur; 47 } else { 48 stack.peek().right = cur; 49 } 50 } 51 } 52 stack.push(right); 53 } 54 dfsHelper(stack.pop().left, result); 55 return result; 56 } 57 public int getWeight(int base, String s) { 58 if (s.equals("+") || s.equals("-")) { 59 return base + 1; 60 } 61 if (s.equals("*") || s.equals("/")) { 62 return base + 2; 63 } 64 return Integer.MAX_VALUE; 65 } 66 public void dfsHelper(TreeNode root, ArrayList<String> result) { 67 if (root == null) { 68 return; 69 } 70 if (root.left != null) { 71 dfsHelper(root.left, result); 72 } 73 if (root.right != null) { 74 dfsHelper(root.right, result); 75 } 76 result.add(root.s); 77 } 78 }
Convert Expression to Polish Notation
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an expression string array, return the Polish notation of this expression. (remove the parentheses) Have you met this question in a real interview? Yes Clarification Definition of Polish Notation: http://en.wikipedia.org/wiki/Polish_notation http://baike.baidu.com/view/7857952.htm Example For the expression [(5 − 6) * 7] (which represented by ["(", "5", "−", "6", ")", "*", "7"]), the corresponding polish notation is [* - 5 6 7] (which the return value should be ["*", "−", "5", "6", "7"]).
思路:先Expression Tree Build,然后前序遍历该树就能求出波兰表示法。波兰表示法实际上就是二叉树的前序遍历。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class TreeNode { 2 int val; 3 String s; 4 TreeNode left; 5 TreeNode right; 6 public TreeNode(int val, String s) { 7 this.val = val; 8 this.s = s; 9 left = null; 10 right = null; 11 } 12 } 13 public class Solution { 14 /** 15 * @param expression: A string array 16 * @return: The Polish notation of this expression 17 */ 18 public ArrayList<String> convertToPN(String[] expression) { 19 // write your code here 20 ArrayList<String> result = new ArrayList<>(); 21 if (expression == null || expression.length == 0) { 22 return result; 23 } 24 Stack<TreeNode> stack = new Stack<>(); 25 int val = 0; 26 int base = 0; 27 for (int i = 0; i <= expression.length; i++) { 28 if (i < expression.length) { 29 if (expression[i].equals("(")) { 30 base += 10; 31 continue; 32 } 33 if (expression[i].equals(")")) { 34 base -= 10; 35 continue; 36 } 37 val = getWeight(base, expression[i]); 38 } 39 TreeNode right = i == expression.length ? new TreeNode(Integer.MIN_VALUE, "") : new TreeNode(val, expression[i]); 40 while (!stack.isEmpty() && right.val <= stack.peek().val) { 41 TreeNode cur = stack.pop(); 42 if (stack.isEmpty()) { 43 right.left = cur; 44 } else { 45 if (stack.peek().val < right.val) { 46 right.left = cur; 47 } else { 48 stack.peek().right = cur; 49 } 50 } 51 } 52 stack.push(right); 53 } 54 dfsHelper(stack.pop().left, result); 55 return result; 56 } 57 public int getWeight(int base, String s) { 58 if (s.equals("+") || s.equals("-")) { 59 return base + 1; 60 } 61 if (s.equals("*") || s.equals("/")) { 62 return base + 2; 63 } 64 return Integer.MAX_VALUE; 65 } 66 public void dfsHelper(TreeNode root, ArrayList<String> result) { 67 if (root == null) { 68 return; 69 } 70 result.add(root.s); 71 if (root.left != null) { 72 dfsHelper(root.left, result); 73 } 74 if (root.right != null) { 75 dfsHelper(root.right, result); 76 } 77 } 78 }
Find Peak Element
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
There is an integer array which has the following features: The numbers in adjacent positions are different. A[0] < A[1] && A[A.length - 2] > A[A.length - 1]. We define a position P is a peek if: A[P] > A[P-1] && A[P] > A[P+1] Find a peak element in this array. Return the index of the peak. Notice The array may contains multiple peeks, find any of them. Have you met this question in a real interview? Yes Example Given [1, 2, 1, 3, 4, 5, 7, 6] Return index 1 (which is number 2) or 6 (which is number 7)
思路:根据A[mid]与A[mid - 1](单调性)来二分
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 /** 3 * @param A: An integers array. 4 * @return: return any of peek positions. 5 */ 6 public int findPeak(int[] A) { 7 // write your code here 8 if (A == null || A.length < 3) { 9 return 0; 10 } 11 int start = 0; 12 int end = A.length - 1; 13 while (start + 1 < end) { 14 int mid = start + (end - start) / 2; 15 if (A[mid] < A[mid - 1]) { 16 end = mid; 17 } else { 18 start = mid; 19 } 20 } 21 if (A[start] < A[end]) { 22 return end; 23 } 24 return start; 25 } 26 }
Find Peak Element II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
There is an integer matrix which has the following features: The numbers in adjacent positions are different. The matrix has n rows and m columns. For all i < m, A[0][i] < A[1][i] && A[n - 2][i] > A[n - 1][i]. For all j < n, A[j][0] < A[j][1] && A[j][m - 2] > A[j][m - 1]. We define a position P is a peek if: A[j][i] > A[j+1][i] && A[j][i] > A[j-1][i] && A[j][i] > A[j][i+1] && A[j][i] > A[j][i-1] Find a peak element in this matrix. Return the index of the peak. Notice The matrix may contains multiple peeks, find any of them. Have you met this question in a real interview? Yes Example Given a matrix: [ [1 ,2 ,3 ,6 ,5], [16,41,23,22,6], [15,17,24,21,7], [14,18,19,20,10], [13,14,11,10,9] ] return index of 41 (which is [1,1]) or index of 24 (which is [2,2])
思路I:根据列取最大值,根据上一行对应位置或者下一行对应位置是否比该位置的值大来进行二分。时间复杂度O(nlgn)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 /** 3 * @param A: An integer matrix 4 * @return: The index of the peak 5 */ 6 public List<Integer> findPeakII(int[][] A) { 7 // write your code here 8 List<Integer> result = new ArrayList<>(); 9 if (A == null || A.length == 0 || A[0].length == 0) { 10 return result; 11 } 12 int n = A.length; 13 int m = A[0].length; 14 if (n < 3 || m < 3) { 15 return result; 16 } 17 int start = 0; 18 int end = n - 1; 19 while (start + 1 < end) { 20 int mid = start + (end - start) / 2; 21 int column = findRowPeak(A, mid); 22 if (A[mid][column] < A[mid - 1][column]) { 23 end = mid; 24 } else { 25 start = mid; 26 } 27 } 28 int column = findRowPeak(A, start); 29 if (A[start][column] > A[end][column]) { 30 result.add(start); 31 result.add(column); 32 return result; 33 } 34 column = findRowPeak(A, end); 35 result.add(end); 36 result.add(column); 37 return result; 38 } 39 public int findRowPeak(int[][] A, int index) { 40 int max = A[index][0]; 41 int column = 0; 42 for (int i = 0; i < A[index].length; i++) { 43 if (A[index][i] > max) { 44 max = A[index][i]; 45 column = i; 46 } 47 } 48 return column; 49 } 50 }
思路II:采用思路I,但是交替按照行和列进行二分。时间复杂度O(n)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 /** 3 * @param A: An integer matrix 4 * @return: The index of the peak 5 */ 6 public List<Integer> findPeakII(int[][] A) { 7 // write your code here 8 List<Integer> result = new ArrayList<>(); 9 if (A == null || A.length < 3 || A[0].length < 3) { 10 return result; 11 } 12 int n = A.length; 13 int m = A[0].length; 14 return find(A, 0, n - 1, 0, m - 1, true); 15 } 16 public List<Integer> find(int[][] A, int x1, int x2, int y1, int y2, boolean flag) { 17 if (flag) { 18 int mid = x1 + (x2 - x1) / 2; 19 int index = 0; 20 for (int i = y1; i <= y2; i++) { 21 if (A[mid][i] > A[mid][index]) { 22 index = i; 23 } 24 } 25 if (A[mid - 1][index] > A[mid][index]) { 26 return find(A, x1, mid - 1, y1, y2, !flag); 27 } else if (A[mid + 1][index] > A[mid][index]) { 28 return find(A, mid + 1, x2, y1, y2, !flag); 29 } else { 30 return Arrays.asList(mid, index); 31 } 32 } else { 33 int mid = y1 + (y2 - y1) / 2; 34 int index = 0; 35 for (int i = x1; i <= x2; i++) { 36 if (A[i][mid] > A[index][mid]) { 37 index = i; 38 } 39 } 40 if (A[index][mid - 1] > A[index][mid]) { 41 return find(A, x1, x2, y1, mid - 1, !flag); 42 } else if (A[index][mid + 1] > A[index][mid]) { 43 return find(A, x1, x2, mid + 1, y2, !flag); 44 } else { 45 return Arrays.asList(index, mid); 46 } 47 } 48 } 49 }
Sqrt(x)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Implement int sqrt(int x). Compute and return the square root of x. Have you met this question in a real interview? Yes Example sqrt(3) = 1 sqrt(4) = 2 sqrt(5) = 2 sqrt(10) = 3
思路:从0 ~ x二分答案。易错点:mid * mid很容易超过整数表示范围,所以check的时候记得转为long比较。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 /** 3 * @param x: An integer 4 * @return: The sqrt of x 5 */ 6 public int sqrt(int x) { 7 // write your code here 8 if (x < 0) { 9 return -1; 10 } 11 int start = 0; 12 int end = x; 13 while (start + 1 < end) { 14 int mid = start + (end - start) / 2; 15 if ((long) mid * mid <= (long) x) { 16 start = mid; 17 } else { 18 end = mid; 19 } 20 } 21 if ((long) end * end <= (long) x) { 22 return end; 23 } 24 return start; 25 } 26 }
Sqrt(x) II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Implement double sqrt(double x) and x >= 0. Compute and return the square root of x. 注意事项 You do not care about the accuracy of the result, we will help you to output results. 您在真实的面试中是否遇到过这个题? Yes 样例 Given n = 2 return 1.41421356
思路:从0 ~ x二分答案。易错点:由于二分的是double数,当这个二分数<1时你永远也不可能二分到想要的答案,最后的二分结果就是这个数本身,所以这种情况下end的取值应该置为1才行。还有注意浮点数二分的二分模版上与整数二分的不同,while是end - start是大于某个精度(比题目给定的精度还要小以保证结果的正确性)循环。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param x a double 4 * @return the square root of x 5 */ 6 public double sqrt(double x) { 7 // Write your code here 8 if (x < 0) { 9 return -1; 10 } 11 double start = 0; 12 double end = x; 13 if (end < 1) { 14 end = 1; 15 } 16 while (end - start > 1e-12) { 17 double mid = start + (end - start) / 2; 18 if (mid * mid <= x) { 19 start = mid; 20 } else { 21 end = mid; 22 } 23 } 24 return start; 25 } 26 }
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. Notice 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(n^2). There is only one duplicate number in the array, but it could be repeated more than once. Have you met this question in a real interview? Yes Example Given nums = [5,5,4,3,2,1] return 5 Given nums = [5,4,4,3,2,1] return 4
思路:从可能的答案范围1 ~ n进行二分,当在数组中小于等于mid的元素个数等于mid说明小于等于mid的元素都不是重复元素,反之小于等于mid的元素中含有重复元素。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param nums an array containing n + 1 integers which is between 1 and n 4 * @return the duplicate one 5 */ 6 public int findDuplicate(int[] nums) { 7 // Write your code here 8 if (nums == null || nums.length < 2) { 9 return 0; 10 } 11 int start = 1; 12 int end = nums.length - 1; 13 while (start + 1 < end) { 14 int mid = start + (end - start) / 2; 15 if (check(mid, nums) <= mid) { 16 start = mid; 17 } else { 18 end = mid; 19 } 20 } 21 if (check(start, nums) <= start) { 22 return end; 23 } 24 return start; 25 } 26 public int check(int num, int[] nums) { 27 int result = 0; 28 for (int i = 0; i < nums.length; i++) { 29 if (nums[i] <= num) { 30 result++; 31 } 32 } 33 return result; 34 } 35 }
Wood Cut
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given n pieces of wood with length L[i] (integer array). Cut them into small pieces to guarantee you could have equal or more than k pieces with the same length. What is the longest length you can get from the n pieces of wood? Given L & k, return the maximum length of the small pieces. Notice You couldn't cut wood into float length. If you couldn't get >= k pieces, return 0. Have you met this question in a real interview? Yes Example For L=[232, 124, 456], k=7, return 114.
思路:二分这些木头所能切割成的相等木头的长度,注意二分的区间,既然是长度,范围是1 ~ max(这些木头的长度),不能从0开始,因为所得到的长度不可能是0,0代表不存在这样的长度。Last/Biggest length that can get >= k pieces。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 *@param L: Given n pieces of wood with length L[i] 4 *@param k: An integer 5 *return: The maximum length of the small pieces. 6 */ 7 public int woodCut(int[] L, int k) { 8 // write your code here 9 if (L == null || L.length == 0 || k < 1) { 10 return 0; 11 } 12 int start = 1; 13 int max = 0; 14 for (int i = 0; i < L.length; i++) { 15 max = Math.max(max, L[i]); 16 } 17 int end = max; 18 while (start + 1 < end) { 19 int mid = start + (end - start) / 2; 20 if (count(mid, L) < k) { 21 end = mid; 22 } else { 23 start = mid; 24 } 25 } 26 if (count(end, L) >= k) { 27 return end; 28 } 29 if (count(start, L) >= k) { 30 return start; 31 } 32 return 0; 33 } 34 public int count(int num, int[] L) { 35 int result = 0; 36 for (int i = 0; i < L.length; i++) { 37 result += L[i] / num; 38 } 39 return result; 40 } 41 }
Copy Books
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given n books and the ith book has A[i] pages. You are given k people to copy the n books. n books list in a row and each person can claim a continous range of the n books. For example one copier can copy the books from ith to jth continously, but he can not copy the 1st book, 2nd book and 4th book (without 3rd book). They start copying books at the same time and they all cost 1 minute to copy 1 page of a book. What's the best strategy to assign books so that the slowest copier can finish at earliest time? Have you met this question in a real interview? Yes Example Given array A = [3,2,4], k = 2. Return 5( First person spends 5 minutes to copy book 1 and book 2 and second person spends 4 minutes to copy book 3. )
思路:Smallest time that k people can copy all books。二分抄书所需要的时间,范围是max(抄每本书的时间)~sum(抄每本书的时间),而且答案一定在这个范围内!对每一个二分的抄书时间,判断在这个时间下k个人能不能抄完所有书,这个判断可以转化为在这个时间下所需要的最小抄书人数是不是小于等于k。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param pages: an array of integers 4 * @param k: an integer 5 * @return: an integer 6 */ 7 public int copyBooks(int[] pages, int k) { 8 // write your code here 9 if (pages == null || pages.length == 0 || k < 1) { 10 return 0; 11 } 12 int max = 0; 13 int sum = 0; 14 for (int i = 0; i < pages.length; i++) { 15 max = Math.max(max, pages[i]); 16 sum += pages[i]; 17 } 18 int start = max; 19 int end = sum; 20 while (start + 1 < end) { 21 int mid = start + (end - start) / 2; 22 if (countCopiers(mid, pages) <= k) { 23 end = mid; 24 } else { 25 start = mid; 26 } 27 } 28 if (countCopiers(start, pages) <= k) { 29 return start; 30 } 31 return end; 32 } 33 public int countCopiers(int time, int[] pages) { 34 int copiers = 1; 35 int sum = 0; 36 for (int i = 0; i < pages.length; i++) { 37 if (sum + pages[i] > time) { 38 copiers++; 39 sum = 0; 40 } 41 sum += pages[i]; 42 } 43 return copiers; 44 } 45 }
Maximum Average Subarray
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array with positive and negative numbers, find the maximum average subarray which length should be greater or equal to given length k. Notice It's guaranteed that the size of the array is greater or equal to k. Have you met this question in a real interview? Yes Example Given nums = [1, 12, -5, -6, 50, 3], k = 3 Return 15.667 // (-6 + 50 + 3) / 3 = 15.667
思路:涉及子数组问题用到prefix_sum,sum[i] = sum[i - 1] + num[i - 1]。最大平均和转化为原数组中每一个元素减去二分的mid所得到的数组中是否有一段长度大于等于k的subArray的和大于等于0.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param nums an array with positive and negative numbers 4 * @param k an integer 5 * @return the maximum average 6 */ 7 public double maxAverage(int[] nums, int k) { 8 // Write your code here 9 if (nums == null || nums.length == 0 || k < 1) { 10 return 0; 11 } 12 double min = Integer.MAX_VALUE; 13 double max = Integer.MIN_VALUE; 14 for (int i = 0; i < nums.length; i++) { 15 min = Math.min(min, nums[i]); 16 max = Math.max(max, nums[i]); 17 } 18 double start = min; 19 double end = max; 20 while (end - start > 1e-6) { 21 double mid = start + (end - start) / 2; 22 if (check(nums, mid, k)) { 23 start = mid; 24 } else { 25 end = mid; 26 } 27 } 28 return start; 29 } 30 public boolean check(int[] nums, double mid, int k) { 31 double[] sum = new double[nums.length + 1]; 32 sum[0] = 0; 33 double min_pre = 0; 34 for (int i = 1; i < sum.length; i++) { 35 sum[i] = sum[i - 1] + nums[i - 1] - mid; 36 if (i >= k && sum[i] - min_pre >= 0) { 37 return true; 38 } 39 if (i >= k) { 40 min_pre = Math.min(min_pre, sum[i - k + 1]); 41 } 42 } 43 return false; 44 } 45 }
第5周 & 第6周 ---- Dynamic Programming
Maximal Square
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and return its area. Have you met this question in a real interview? Yes Example 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 4.
思路:1.状态 dp[i][j] 表示以i和j作为正方形右下角可以拓展的最大边长
2.方程 if matrix[i][j] == 1 dp[i][j] = min(left[i][j-1], up[i-1][j], dp[i-1][j-1]) + 1
(upper[i][j] 表示以i, j 作为全为1的向上长条有多长,left[i][j] 表示以i, j 作为全为1的向左长条有多长,)
if matrix[i][j] == 0 dp[i][j] = 0
然而我们可以将上面这个状态转移方程简化如下面这个:
if matrix[i][j] == 1 dp[i][j] = min(dp[i - 1][j], dp[i][j-1], dp[i-1][j-1]) + 1;
if matrix[i][j] == 0 dp[i][j] = 0
3.初始化 f[i][0] = matrix[i][0];
f[0][j] = matrix[0][j]
4.答案 max{dp[i][j]} * max{dp[i][j]}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param matrix: a matrix of 0 and 1 4 * @return: an integer 5 */ 6 public int maxSquare(int[][] matrix) { 7 // write your code here 8 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 9 return 0; 10 } 11 int n = matrix.length; 12 int m = matrix[0].length; 13 int[][] dp = new int[2][m]; 14 int result = 0; 15 for (int i = 0; i < n; i++) { 16 dp[i % 2][0] = matrix[i % 2][0]; 17 if (dp[i % 2][0] > result) { 18 result = dp[i % 2][0]; 19 } 20 } 21 for (int j = 0; j < m; j++) { 22 dp[0][j] = matrix[0][j]; 23 if (dp[0][j] > result) { 24 result = dp[0][j]; 25 } 26 } 27 for (int i = 1; i < n; i++) { 28 for (int j = 1; j < m; j++) { 29 if (matrix[i % 2][j] == 1) { 30 dp[i % 2][j] = Math.min(Math.min(dp[(i - 1) % 2][j], dp[i % 2][j - 1]), dp[(i - 1) % 2][j - 1]) + 1; 31 if (dp[i % 2][j] > result) { 32 result = dp[i % 2][j]; 33 } 34 } else { 35 dp[i % 2][j] = 0; 36 } 37 } 38 } 39 return result * result; 40 } 41 }
Maximal Square II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a 2D binary matrix filled with 0's and 1's, find the largest square which diagonal is all 1 and others is 0. Have you met this question in a real interview? Yes Example For example, given the following matrix: 1 0 1 0 0 1 0 0 1 0 1 1 0 0 1 1 0 0 1 0 Return 9
思路:1.状态 dp[i][j] 表示以i和j作为正方形右下角可以拓展的最大边长
upper[i][j] 表示以i, j 作为全为0的向上长条有多长
left[i][j] 表示以i, j 作为全为0的向左长条有多长
2.方程 if matrix[i][j] == 1 dp[i][j] = 1 + Math.min(dp[i - 1][j - 1], Math.min(up[i - 1][j], left[i][j - 1]))
up[i][j] = 0
left[i][j] = 0
if matrix[i][j] == 0 dp[i][j] = 0
up[i][j] = 1 + up[i - 1][j]
left[i][j] = 1 + left[i][j - 1]
3.初始化 不需要
4.答案 max{dp[i][j]} * max{dp[i][j]}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param matrix a matrix of 0 and 1 4 * @return an integer 5 */ 6 public int maxSquare2(int[][] matrix) { 7 // write your code here 8 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 9 return 0; 10 } 11 int n = matrix.length; 12 int m = matrix[0].length; 13 int[][] dp = new int[n][m]; 14 int[][] up = new int[n][m]; 15 int[][] left = new int[n][m]; 16 int length = 0; 17 for (int i = 0; i < n; i++) { 18 for (int j = 0; j < m; j++) { 19 if (matrix[i][j] == 0) { 20 dp[i][j] = 0; 21 up[i][j] = 1; 22 left[i][j] = 1; 23 if (i > 0) { 24 up[i][j] += up[i - 1][j]; 25 } 26 if (j > 0) { 27 left[i][j] += left[i][j - 1]; 28 } 29 } else { 30 dp[i][j] = 1; 31 if (i > 0 && j > 0) { 32 dp[i][j] = 1 + Math.min(dp[i - 1][j - 1], 33 Math.min(up[i - 1][j], left[i][j - 1])); 34 } 35 up[i][j] = 0; 36 left[i][j] = 0; 37 } 38 length = Math.max(length, dp[i][j]); 39 } 40 } 41 return length * length; 42 } 43 }
区间类DP
特点:1. 求一段区间的解max/min/count
2. 转移方程通过区间更新
3. 从大到小的更新
Stone Game
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
There is a stone game.At the beginning of the game the player picks n piles of stones in a line. The goal is to merge the stones in one pile observing the following rules: At each step of the game,the player can merge two adjacent piles to a new pile. The score is the number of stones in the new pile. You are to determine the minimum of the total score. Have you met this question in a real interview? Yes Example For [4, 1, 1, 4], in the best solution, the total score is 18: 1. Merge second and third piles => [4, 2, 4], score +2 2. Merge the first two piles => [6, 4],score +6 3. Merge the last two piles => [10], score +10 Other two examples: [1, 1, 1, 1] return 8 [4, 4, 5, 9] return 43
思路:1.状态 dp[i][j] 表示把第i到第j个石子合并到一起的最小花费
2.方程 dp[i][j] = min(dp[i][k]+dp[k+1][j]+sum[i,j]) (记得提前初始化sum[i][j]) for k: i ~ j - 1
3.初始化 dp[i][i] = 0
4.答案 dp[0][n-1]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param A an integer array 4 * @return an integer 5 */ 6 public int stoneGame(int[] A) { 7 // Write your code here 8 if (A == null || A.length == 0) { 9 return 0; 10 } 11 int n = A.length; 12 int[][] sum = new int[n][n]; 13 for (int i = 0; i < n; i++) { 14 for (int j = i; j < n; j++) { 15 if (j == i) { 16 sum[i][j] = A[j]; 17 } else { 18 sum[i][j] = sum[i][j - 1] + A[j]; 19 } 20 } 21 } 22 int[][] dp = new int[n][n]; 23 for (int i = 0; i < n; i++) { 24 dp[i][i] = 0; 25 } 26 for (int len = n - 1; len >= 1; len--) { 27 for (int i = 0, j = n - len; i < len; i++, j++) { 28 dp[i][j] = Integer.MAX_VALUE; 29 for (int k = i; k < j; k++) { 30 dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum[i][j]); 31 } 32 } 33 } 34 return dp[0][n - 1]; 35 } 36 }
Stone Game II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
There is a stone game.At the beginning of the game the player picks n piles of stones in a circle. The goal is to merge the stones in one pile observing the following rules: At each step of the game,the player can merge two adjacent piles to a new pile. The score is the number of stones in the new pile. You are to determine the minimum of the total score. Have you met this question in a real interview? Yes Example For [1, 4, 4, 1], in the best solution, the total score is 18: 1. Merge second and third piles => [2, 4, 4], score +2 2. Merge the first two piles => [6, 4],score +6 3. Merge the last two piles => [10], score +10 Other two examples: [1, 1, 1, 1] return 8 [4, 4, 5, 9] return 43
思路:数组变成循环数组。解决循环方式有三种:1.拆环 2.复制 3.取反
这里采用复制数组的方式解决循环问题。复制前len - 1个元素到原数组末尾,对得到的新数组用Stone Game中一样的方法DP,最后在得到的dp数组中找到长度为len并且score最小子数组。
1.状态 dp[i][j] 表示把第i到第j个石子合并到一起的最小花费 (dp[2 * n - 1][2 * n - 1])
2.方程 dp[i][j] = min(dp[i][k]+dp[k+1][j]+sum[i,j]) (记得提前初始化sum[i][j]) for k: i ~ j - 1
3.初始化 dp[i][i] = 0
4.答案 dp[i][i + n - 1] for i : 0 ~ n - 1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param A an integer array 4 * @return an integer 5 */ 6 public int stoneGame2(int[] A) { 7 // Write your code here 8 if (A == null || A.length == 0) { 9 return 0; 10 } 11 int n = A.length; 12 int[][] sum = new int[2 * n - 1][2 * n - 1]; 13 for (int i = 0; i < 2 * n - 1; i++) { 14 for (int j = i; j < 2 * n - 1; j++) { 15 if (j == i) { 16 sum[i][j] = A[j % n]; 17 } else { 18 sum[i][j] = sum[i][j - 1] + A[j % n]; 19 } 20 } 21 } 22 int[][] dp = new int[2 * n - 1][2 * n - 1]; 23 for (int i = 0; i < 2 * n - 1; i++) { 24 dp[i][i] = 0; 25 } 26 for (int len = 2 * n - 2; len >= 1; len--) { 27 for (int i = 0, j = 2 * n - 1 - len; i < len; i++, j++) { 28 dp[i][j] = Integer.MAX_VALUE; 29 for (int k = i; k < j; k++) { 30 dp[i][j] = Math.min(dp[i][j], 31 dp[i][k] + dp[k + 1][j] + sum[i][j]); 32 } 33 } 34 } 35 int result = Integer.MAX_VALUE; 36 for (int i = 0; i < n; i++) { 37 result = Math.min(result, dp[i][i + n - 1]); 38 } 39 return result; 40 } 41 }
Copy Books
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given n books and the ith book has A[i] pages. You are given k people to copy the n books. n books list in a row and each person can claim a continous range of the n books. For example one copier can copy the books from ith to jth continously, but he can not copy the 1st book, 2nd book and 4th book (without 3rd book). They start copying books at the same time and they all cost 1 minute to copy 1 page of a book. What's the best strategy to assign books so that the slowest copier can finish at earliest time? Have you met this question in a real interview? Yes Example Given array A = [3,2,4], k = 2. Return 5( First person spends 5 minutes to copy book 1 and book 2 and second person spends 4 minutes to copy book 3. )
思路I:二分法。二分答案,即二分抄完书所花费的最短时间。初始范围max ~ sum。如果这些人在二分的时间mid内抄完书所用的最少人数(贪心来求该人数)<=k,说明能完成任务,时间end=mid;否则start = mid。最后Double Check。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param pages: an array of integers 4 * @param k: an integer 5 * @return: an integer 6 */ 7 public int copyBooks(int[] pages, int k) { 8 // write your code here 9 if (pages == null || pages.length == 0 || k < 1) { 10 return 0; 11 } 12 int max = 0; 13 int sum = 0; 14 for (int i = 0; i < pages.length; i++) { 15 max = Math.max(max, pages[i]); 16 sum += pages[i]; 17 } 18 int start = max; 19 int end = sum; 20 while (start + 1 < end) { 21 int mid = start + (end - start) / 2; 22 if (countCopiers(mid, pages) <= k) { 23 end = mid; 24 } else { 25 start = mid; 26 } 27 } 28 if (countCopiers(start, pages) <= k) { 29 return start; 30 } 31 return end; 32 } 33 public int countCopiers(int time, int[] pages) { 34 int copiers = 1; 35 int sum = 0; 36 for (int i = 0; i < pages.length; i++) { 37 if (sum + pages[i] > time) { 38 copiers++; 39 sum = 0; 40 } 41 sum += pages[i]; 42 } 43 return copiers; 44 } 45 }
思路II:动态规划。思路同Copy Books II。需要预先求得一个人抄完前i本书所花费的时间sum[i]数组,为状态方程做准备。
1.状态 dp[i][j] 表示前i+1个人,抄前j本书的最小完成时间。
2.方程 dp[i][j] = min{max{dp[i - 1][j - k], sum[j] - sum[j - k]}} for k:0 ~ j
3.初始化 dp[0][j] = sum[j] for j:0 ~ n; dp[i][0] = 0 for i:0 ~ len - 1
4.答案 dp[len - 1][n]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param pages: an array of integers 4 * @param k: an integer 5 * @return: an integer 6 */ 7 public int copyBooks(int[] pages, int k) { 8 // write your code here 9 if (pages == null || pages.length == 0) { 10 return 0; 11 } 12 if (k < 1) { 13 return Integer.MAX_VALUE; 14 } 15 int n = pages.length; 16 int[] sum = new int[n + 1]; 17 for (int i = 1; i <= n; i++) { 18 sum[i] = sum[i - 1] + pages[i - 1]; 19 } 20 int[][] dp = new int[2][n + 1]; 21 for (int i = 0; i <= n; i++) { 22 dp[0][i] = sum[i]; 23 } 24 for (int i = 1; i < k; i++) { 25 for (int j = 1; j <= n; j++) { 26 dp[i % 2][j] = Integer.MAX_VALUE; 27 for (int t = 0; t <= j; t++) { 28 if (dp[(i - 1) % 2][j - t] < sum[j] - sum[j - t]) { 29 dp[i % 2][j] = Math.min(dp[i % 2][j], 30 sum[j] - sum[j - t]); 31 break; 32 } else { 33 dp[i % 2][j] = Math.min(dp[i % 2][j], 34 dp[(i - 1) % 2][j - t]); 35 } 36 } 37 } 38 } 39 return dp[(k - 1) % 2][n]; 40 } 41 }
Copy Books II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given n books( the page number of each book is the same) and an array of integer with size k means k people to copy the book and the i th integer is the time i th person to copy one book). You must distribute the continuous id books to one people to copy. (You can give book A[1],A[2] to one people, but you cannot give book A[1], A[3] to one people, because book A[1] and A[3] is not continuous.) Return the number of smallest minutes need to copy all the books. Have you met this question in a real interview? Yes Example Given n = 4, array A = [3,2,4], . Return 4( First person spends 3 minutes to copy book 1, Second person spends 4 minutes to copy book 2 and 3, Third person spends 4 minutes to copy book 4. )
思路:1.状态 dp[i][j] 表示前i+1个人,抄前j本书的最小完成时间。
2.方程 dp[i][j] = min{max{dp[i - 1][j - k], k * times[i]}} for k:0 ~ j
3.初始化 dp[0][j] = j * times[0] for j:0 ~ n;dp[i][0] = 0 for i:0 ~ len - 1
4.答案 dp[len-1][n]
注意:这道题卡时间复杂度和空间复杂度,代码中要注意的地方已经标记。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param n: an integer 4 * @param times: an array of integers 5 * @return: an integer 6 */ 7 public int copyBooksII(int n, int[] times) { 8 // write your code here 9 if (n <= 0) { 10 return 0; 11 } 12 if (times == null || times.length == 0) { 13 return Integer.MAX_VALUE; 14 } 15 int len = times.length; 16 // 这道题对空间复杂度卡的比较严,当人数很多时空间复杂度过大,所以要用滚动数组优化。 17 int[][] dp = new int[2][n + 1]; 18 for (int i = 0; i <= n; i++) { 19 dp[0][i] = i * times[0]; 20 } 21 for (int i = 1; i < len; i++) { 22 for (int j = 1; j <= n; j++) { 23 dp[i % 2][j] = Integer.MAX_VALUE; 24 for (int k = 0; k <= j; k++) { 25 if (dp[(i - 1) % 2][j - k] < k * times[i]) { 26 dp[i % 2][j] = Math.min(dp[i % 2][j], k * times[i]); 27 // 这道题对时间复杂度卡的比较严,当进入改if判断语句内说明此时第i + 1个人抄书的时间占大头, 28 // 因此再循环时还会进入改if语句,并且这个时间会越来越大,所以这个if语句只需要进来一次就可以break了。 29 // 实际上当k比较小的时候进入else语句,前i个人的抄书时间占大头;当k较大的时候,进入if语句, 30 // 第i + 1个人抄书的时间占大头,而且if语句执行一次后就break即可。相当于剪枝来优化时间复杂度。 31 break; 32 } else { 33 dp[i % 2][j] 34 = Math.min(dp[i % 2][j], dp[(i - 1) % 2][j - k]); 35 } 36 } 37 38 } 39 } 40 return dp[(len - 1) % 2][n]; 41 } 42 }
Post Office Problem
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
On one line there are n houses. Give you an array of integer means the the position of each house. Now you need to pick k position to build k post office, so that the sum distance of each house to the nearest post office is the smallest. Return the least possible sum of all distances between each village and its nearest post office. Have you met this question in a real interview? Yes Example Given array a = [1,2,3,4,5], k = 2. return 3.
思路:
dp[i][j]表示在前i个村庄中建j个post的最短距离,l为分隔点,可以将问题转化为在前l个村庄建j-1个post的最短距离+在第l+1到第i个村庄建1个post的最短距离。其中有个性质,如元素是单调排列的,则在中间位置到各个元素的距离和最小。
-
初始化dis矩阵,枚举不同开头和结尾的村庄之间建1个post的最小距离,即求出开头和结尾村庄的中间点,然后计算开头到结尾的所有点到中间点的距离。记得要对原矩阵排序,这样才能用中间点距离最小性质。
-
初始化dp矩阵,即初始化dp[i][1],求前i个村庄建1个post的最小距离(可根据dis求出)。
-
post数l从2枚举到k,开始村庄i从j枚举到结尾(因为要建j个post至少需要j个村庄,否则没有意义,其实可以从j + 1开始枚举),然后根据状态函数求dp[i][j],分割点l从j-1枚举到i-1(前l个村庄建j-1个post则至少需要j-1个村庄),在这些分隔点的情况下求dp[i][j]的最小值。
4.返回dp[n][k]即可。
1.状态 dp[i][j] 表示前i个村庄中建j个邮局的最短距离
2.方程 dp[i][j] = min{dp[l][j - 1] + dis[l + 1][i] for j - 1 <= l < i}
3.初始化 dp[i][1] = dis[1][i] for 1 <= i <= n
4.答案 dp[n][k]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param A an integer array 4 * @param k an integer 5 * @return an integer 6 */ 7 public int postOffice(int[] A, int k) { 8 // Write your code here 9 if (A == null || A.length == 0 || k <= 0 || k >= A.length) { 10 return 0; 11 } 12 //一个Array只有单调才满足中间的数和其它所有数的差的绝对值之和最小 13 Arrays.sort(A); 14 int[][] dis = initial(A); 15 int n = A.length; 16 int[][] dp = new int[n + 1][k + 1]; 17 for (int i = 1; i <= n; i++) { 18 dp[i][1] = dis[1][i]; 19 } 20 for (int j = 2; j <= k; j++) { 21 for (int i = j + 1; i <= n; i++) { 22 dp[i][j] = Integer.MAX_VALUE; 23 for (int l = j - 1; l < i; l++) { 24 dp[i][j] = Math.min(dp[i][j], dp[l][j - 1] + dis[l + 1][i]); 25 } 26 } 27 } 28 return dp[n][k]; 29 } 30 public int[][] initial(int[] A) { 31 int n = A.length; 32 int[][] dis = new int[n + 1][n + 1]; 33 for (int i = 1; i < n; i++) { 34 for (int j = i + 1; j <= n; j++) { 35 int mid = (i + j) / 2; 36 for (int k = i; k <= j; k++) { 37 dis[i][j] += Math.abs(A[k - 1] - A[mid - 1]); 38 } 39 } 40 } 41 return dis; 42 } 43 }
双序列DP
1. state: f[i][j]代表了第一个sequence的前i个数字/字符,配上第二个sequence的前j个...
2. function: f[i][j] = 研究第i个和第j个的匹配关系
3. initialize: f[i][0] 和 f[0][i]
4. answer: f[n][m] min/max/数目/存在关系
5. n = s1.length() m = s2.length()
解题技巧画矩阵,填写矩阵
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: Insert a character Delete a character Replace a character Have you met this question in a real interview? Yes Example Given word1 = "mart" and word2 = "karma", return 3.
思路: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 /** 3 * @param word1 & word2: Two string. 4 * @return: The minimum number of steps. 5 */ 6 public int minDistance(String word1, String word2) { 7 // write your code here 8 if (word1 == null || word2 == null) { 9 return 0; 10 } 11 int n = word1.length(); 12 int m = word2.length(); 13 int[][] dp = new int[2][m + 1]; 14 for (int j = 0; j < m + 1; j++) { 15 dp[0][j] = j; 16 } 17 for (int i = 1; i < n + 1; i++) { 18 for (int j = 0; j < m + 1; j++) { 19 if (j == 0) { 20 dp[i % 2][j] = i; 21 } else { 22 if (word1.charAt(i - 1) == word2.charAt(j - 1)) { 23 dp[i % 2][j] = dp[(i - 1) % 2][j - 1]; 24 } else { 25 dp[i % 2][j] = 26 Math.min(dp[(i - 1) % 2][j - 1], 27 Math.min(dp[(i - 1) % 2][j], 28 dp[i % 2][j - 1])) + 1; 29 } 30 } 31 } 32 } 33 return dp[n % 2][m]; 34 } 35 }
K Edit Distance
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a set of strings which just has lower case letters and a target string, output all the strings for each the edit distance with the target no greater than k. You have the following 3 operations permitted on a word: Insert a character Delete a character Replace a character Have you met this question in a real interview? Yes Example Given words = ["abc", "abd", "abcd", "adc"] and target = "ac", k = 1 Return ["abc", "adc"]
思路:暴力法,按Edit Distance思路对每个word计算最短编辑距离然后判断该距离是否小于等于k,不可取。考虑到这些word有很多重复,因此这些word可以用trie树来存储,从而减少重复字母的最小编辑距离的计算。也就是在trie树上跑动态规划。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class TrieNode { 2 Map<Character, TrieNode> children; 3 boolean hasWord; 4 String word; 5 public TrieNode() { 6 children = new HashMap<>(); 7 hasWord = false; 8 word = null; 9 } 10 } 11 class Trie { 12 TrieNode root; 13 public Trie() { 14 root = new TrieNode(); 15 } 16 public void insert(String word) { 17 TrieNode cur = root; 18 char[] wordArray = word.toCharArray(); 19 for (char c : wordArray) { 20 if (!cur.children.containsKey(c)) { 21 cur.children.put(c, new TrieNode()); 22 } 23 cur = cur.children.get(c); 24 } 25 cur.hasWord = true; 26 cur.word = word; 27 } 28 } 29 public class Solution { 30 /** 31 * @param words a set of stirngs 32 * @param target a target string 33 * @param k an integer 34 * @return output all the strings that meet the requirements 35 */ 36 public List<String> kDistance(String[] words, String target, int k) { 37 // Write your code here 38 List<String> result = new ArrayList<>(); 39 if (words == null) { 40 return result; 41 } 42 Trie trie = new Trie(); 43 for (int i = 0; i < words.length; i++) { 44 trie.insert(words[i]); 45 } 46 int n = target.length(); 47 int[] dp = new int[n + 1]; 48 for (int i = 0; i <= n; i++) { 49 dp[i] = i; 50 } 51 find(trie.root, target, dp, k, result); 52 return result; 53 } 54 public void find(TrieNode node, String target, int[] dp, int k, 55 List<String> result) { 56 int n = target.length(); 57 if (node.hasWord && dp[n] <= k) { 58 result.add(node.word); 59 } 60 int[] next = new int[n + 1]; 61 for (Character c : node.children.keySet()) { 62 next[0] = dp[0] + 1; 63 for (int i = 1; i <= n; i++) { 64 if (c == target.charAt(i - 1)) { 65 next[i] = dp[i - 1]; 66 } else { 67 next[i] = 1 + Math.min(Math.min(dp[i - 1], dp[i]), 68 next[i - 1]); 69 } 70 } 71 find(node.children.get(c), target, next, k, result); 72 } 73 } 74 }
动态规划 & 贪心
121. Best Time to Buy and Sell Stock
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Say you have an array for which the ith element is the price of a given stock on day i. If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit. Example 1: Input: [7, 1, 5, 3, 6, 4] Output: 5 max. difference = 6-1 = 5 (not 7-1 = 6, as selling price needs to be larger than buying price) Example 2: Input: [7, 6, 4, 3, 1] Output: 0 In this case, no transaction is done, i.e. max profit = 0.
思路I:动态规划 动态规划的一般方式是利用一个数组dp[]用来存储如果以今天结尾, 能得到的结果。 对于我们这个问题来说, 就是用来存储到第i天的为止, 如果只能买卖一次股票, 所能获得的最大收益。 这个收益和截止到前一天为止的最大收益有关 — 或者说是昨天的收益有关, 我们将这个收益称为A。 它还和今天的股价有关, 确切的说, 是和今天的股价和截止到今天为止的最小股价的差有关, 这个差即为如果今天卖出, 可以得到 的最大收益, 我们将这个收益称为B。 今天的最终最大收益则为A和B中大的那个。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maxProfit(int[] prices) { 3 if (prices == null || prices.length <= 1) { 4 return 0; 5 } 6 int n = prices.length; 7 int[] dp = new int[n + 1]; 8 int min = prices[0]; 9 for (int i = 2; i <= n; i++) { 10 dp[i] = Math.max(dp[i - 1], prices[i - 1] - min); 11 min = Math.min(min, prices[i - 1]); 12 } 13 return dp[n]; 14 } 15 }
思路II:贪心 如果用贪心的思想, 我们不需要保存每天的状态, 而只需要保存截至到目前为止最大的收益即可, 则最后一天的最大的收益即为最后的收益。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maxProfit(int[] prices) { 3 if (prices == null || prices.length <= 1) { 4 return 0; 5 } 6 int min = prices[0]; 7 int max = 0; 8 for (int i = 1; i < prices.length; i++) { 9 min = Math.min(min, prices[i - 1]); 10 max = Math.max(max, prices[i] - min); 11 } 12 return max; 13 } 14 }
122. Best Time to Buy and Sell Stock II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Say you have an array for which the ith element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
这道题与前一道题的区别在于, 我们可以买卖股票很多次(as many as you want), 求最大收益。
这道题相对简单,不要把自己绕进去,其实只要比较是不是今天比昨天贵, 不用管之前的情况。举个简单的栗子,比如考虑第三天的情况的时候, 因为day3 - day1 = day3 - day2 + day2 - day1, 而day2 - day1 又已经包含在dp[day2]中,所以,只要day3比day2大,则,dp[day3] = dp[day2] + day3 - day2
思路I:动态规划
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maxProfit(int[] prices) { 3 if (prices == null || prices.length <= 1) { 4 return 0; 5 } 6 int n = prices.length; 7 int[] dp = new int[n + 1]; 8 int max = 0; 9 for (int i = 2; i <= n; i++) { 10 if (prices[i - 1] > prices[i - 2]) { 11 dp[i] = dp[i - 1] + prices[i - 1] - prices[i - 2]; 12 } else { 13 dp[i] = dp[i - 1]; 14 } 15 } 16 return dp[n]; 17 } 18 }
思路II:贪心
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maxProfit(int[] prices) { 3 if (prices == null || prices.length <= 1) { 4 return 0; 5 } 6 int result = 0; 7 for (int i = 1; i < prices.length; i++) { 8 if (prices[i] > prices[i - 1]) { 9 result += prices[i] - prices[i - 1]; 10 } 11 } 12 return result; 13 } 14 }
309. Best Time to Buy and Sell Stock with Cooldown
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Say you have an array for which the ith element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions: You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day) Example: prices = [1, 2, 3, 0, 2] maxProfit = 3 transactions = [buy, sell, cooldown, buy, sell]
思路:动态规划
很好的一道题, 很容易把自己绕乎进去。
每天都有两种可能性,要么卖,要么啥都不干。 当然这天的最大收益则是这两个方案中大的那个。 按照DP的思想,不如开两个数组,一个表示今天要卖,另一个表示今天啥也不干。
profitDoNothing[i]比较容易想,因为今天啥也不干,所以今天就继承昨天的结果,昨天的结果又有两种可能 --- 卖、啥也不干,当然要继承它俩中大的那个,所以profitDoNothing[i] = Math.max(profitSell[i-1], profitDoNothing[i-1])。
重点来了,profitSell[i]怎么算。其实也有两种情况,如果day[i-1]我买入了(或者卖了,因为卖完了可以再买回来),则day[i]可以直接卖,收益是:profitSell[i] = profitSell[i-1] + prices[i] - prices[i-1]。但是还有一种情况,就是day[i-1]是啥也没干的,所以day[i]需要把自己先买回来,再卖,收益是:profitSell[i] = profitDoNothing[i-1] + prices[i] - prices[i] = profitDoNothing[i-1]。最终取这两个大的就行了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maxProfit(int[] prices) { 3 if (prices == null || prices.length <= 1) { 4 return 0; 5 } 6 int n = prices.length; 7 int[] profitDoNothing = new int[n + 1]; 8 int[] profitSell = new int[n + 1]; 9 profitDoNothing[0] = 0; 10 profitDoNothing[1] = 0; 11 profitSell[0] = 0; 12 profitSell[1] = 0; 13 for (int i = 2; i <= n; i++) { 14 profitDoNothing[i] = Math.max(profitDoNothing[i - 1], profitSell[i - 1]); 15 profitSell[i] = Math.max(profitSell[i - 1] + prices[i- 1] - prices[i - 2], profitDoNothing[i - 1]); 16 } 17 return Math.max(profitDoNothing[n], profitSell[n]); 18 } 19 }
思路II:贪心
仔细看你的动态规划的状态转移方程,你会发现其实你不需要一个数组,你只需要两个数: profitSell & profitDoNothing 然后每次比较的时候创建一个中间变量即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maxProfit(int[] prices) { 3 if (prices == null || prices.length <= 1) { 4 return 0; 5 } 6 int n = prices.length; 7 int profitDoNothing = 0; 8 int profitSell = 0; 9 for (int i = 1; i < n; i++) { 10 int temp = profitDoNothing; 11 profitDoNothing = Math.max(profitSell, profitDoNothing); 12 profitSell = Math.max(profitSell + prices[i] - prices[i - 1], temp); 13 } 14 return Math.max(profitDoNothing, profitSell); 15 } 16 }
123. Best Time to Buy and Sell Stock III
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Say you have an array for which the ith element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete at most two transactions. Note: You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
思路:动态规划
使用”局部最优解和全局最优解”的思想, 和Cooldown那道题的思路很像, 每一天将有两个状态 – “卖出”和”不卖出”。 利用两个动态规划数组local和global分别来存储局部最优解和全局最优解, 难点在于 这道题的状态更复杂, 因为要在两个维度计算, 第一个维度是天数的维度, 第二个维度是交易次数的维度, 所以local和global都是二维数组。 其中, global[i][j]表示到第i天最多进行j次交易可以达到的最大利润,而 local[i][j]表示到第i天最多进行j次交易,并且在第i天卖出可以达到的最大利润。
则: global[i][j] = Math.max(local[i][j], global[i-1][j]) 也就是到今天为止的全局最好,是到今天为止的局部最好与到昨天的全局最好中大的那个。因为今天的最大利润有两种情况,第一是几天卖出了(既local[i][j]),另一种是今天什么都没做,那今天的最好的情况就是昨天的全局最好情况
而: local[i][j] = Math.max(global[i-1][j-1] + prices[i] - prices[i-1], local[i-1][j] + prices[i] - prices[i-1]) — 这里是local[i-1][j]因为就算第i-1天卖了, 可以再买回来在第i天再卖,所以并不增加transaction。 这里就不太好理解了, 因为今天需要卖出,所以需 要加prices[i] - prices[i-1], 两种加的可能性,(1) 和全局比,(2)和局部比。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maxProfit(int[] prices) { 3 if (prices == null || prices.length <= 1) { 4 return 0; 5 } 6 int n = prices.length; 7 int[][] local = new int[n][3]; 8 int[][] global = new int[n][3]; 9 for (int i = 1; i < n; i ++) { 10 int profitToday = prices[i] - prices[i - 1]; 11 for (int j = 1; j <= 2; j++) { 12 local[i][j] = Math.max(local[i - 1][j] + profitToday, global[i - 1][j - 1] + profitToday); 13 global[i][j] = Math.max(local[i][j], global[i - 1][j]); 14 } 15 } 16 return global[n - 1][2]; 17 } 18 }
思路:贪心
如果用贪心的做法, 则不需要维护天数的维度, 因为我们只需要记录每天的local和global即可, 后一天的根据前一天的结果进行计算。 如果只是两次操作,相当于j = 2。但是贪心的时候有一个小技巧需要注意一下, 就是我们需要倒序的算, 因为我们其实需要和前一天第2次操作的比, 所以我们不希望这个值被覆盖掉。
这里倒序的原因是不想让global[k]被覆盖掉 --- 这里也是难理解的一个点, 因为我们简化了二维数组,而选择只用一维数组
所以相当于这个数组只维护了前一天的信息,而今天的量也只和前一天有关,而我们只care最后一天k次交易的情况,这个情况跟前一天的k-1次交易有关,所以为了不让这个值被覆盖掉,我们选择倒叙
如果用二维数组则没有这个问题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maxProfit(int[] prices) { 3 if (prices == null || prices.length <= 1) { 4 return 0; 5 } 6 int n = prices.length; 7 int[] local = new int[3]; 8 int[] global = new int[3]; 9 for (int i = 1; i < n; i++) { 10 int profitToday = prices[i] - prices[i - 1]; 11 for (int j = 2; j >= 1; j--) { 12 local[j] = Math.max(local[j] + profitToday, global[j - 1] + profitToday); 13 global[j] = Math.max(local[j], global[j]); 14 } 15 } 16 return global[2]; 17 } 18 }
188. Best Time to Buy and Sell Stock IV
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Say you have an array for which the ith element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete at most k transactions. Note: You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
思路I:动态规划
第四题是第三题的推广, 由第三题中只能卖两次, 推广到现在可以卖k次。 思路和第三题是一样的, 我们在这里不再赘述。
但是需要注意的一点技巧是, 需要注意一个细节, 当k比prices.length的一半还要大的时候, 说明我们无法买卖够k次, 那么这道题就转变为了第二题。 这一点需要提前考虑到, 否则会得到一个TLE。不考虑这一点的话,当k很大的时候时间复杂度就比较高了!O(n * k)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maxProfit(int k, int[] prices) { 3 if (prices == null || prices.length <= 1) { 4 return 0; 5 } 6 int n = prices.length; 7 if (k > n / 2) { 8 int[] dp = new int[n + 1]; 9 for (int i = 2; i <= n; i++) { 10 if (prices[i - 1] > prices[i - 2]) { 11 dp[i] = dp[i - 1] + prices[i - 1] - prices[i - 2]; 12 } else { 13 dp[i] = dp[i - 1]; 14 } 15 } 16 return dp[n]; 17 } 18 int[][] local = new int[n][k + 1]; 19 int[][] global = new int[n][k + 1]; 20 for (int i = 1; i < n; i++) { 21 int profitToday = prices[i] - prices[i - 1]; 22 for (int j = 1; j <= k; j++) { 23 local[i][j] = Math.max(local[i - 1][j] + profitToday, global[i - 1][j - 1] + profitToday); 24 global[i][j] = Math.max(local[i][j], global[i - 1][j]); 25 } 26 } 27 return global[n - 1][k]; 28 } 29 }
思路II:贪心
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public int maxProfit(int k, int[] prices) { 3 if (prices == null || prices.length <= 1) { 4 return 0; 5 } 6 int n = prices.length; 7 if (k > n / 2) { 8 int result = 0; 9 for (int i = 1; i < n; i++) { 10 if (prices[i] > prices[i - 1]) { 11 result += prices[i] - prices[i - 1]; 12 } 13 } 14 return result; 15 } 16 int[] local = new int[k + 1]; 17 int[] global = new int[k + 1]; 18 for (int i = 1; i < n; i++) { 19 int profitToday = prices[i] - prices[i - 1]; 20 for (int j = k; j >= 1; j--) { 21 local[j] = Math.max(local[j] + profitToday, global[j - 1] + profitToday); 22 global[j] = Math.max(local[j], global[j]); 23 } 24 } 25 return global[k]; 26 } 27 }
第7周 ---- Follow Up Question
子数组和
Subarray Sum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an integer array, find a subarray where the sum of numbers is zero. Your code should return the index of the first number and the index of the last number. Notice There is at least one subarray that it's sum equals to zero. Have you met this question in a real interview? Yes Example Given [-3, 1, 2, -3, 4], return [0, 2] or [1, 3].
思路:map记录子数组的和与子数组最后一个元素索引的映射,注意一开始map.put(0, 1)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param nums: A list of integers 4 * @return: A list of integers includes the index of the first number 5 * and the index of the last number 6 */ 7 public ArrayList<Integer> subarraySum(int[] nums) { 8 // write your code here 9 ArrayList<Integer> result = new ArrayList<>(); 10 if (nums == null || nums.length == 0) { 11 return result; 12 } 13 Map<Integer, Integer> map = new HashMap<Integer, Integer>(); 14 map.put(0, -1); 15 int sum = 0; 16 for (int i = 0; i < nums.length; i++) { 17 sum += nums[i]; 18 if (map.containsKey(sum)) { 19 result.add(map.get(sum) + 1); 20 result.add(i); 21 return result; 22 } 23 map.put(sum, i); 24 } 25 return result; 26 } 27 }
Subarray Sum II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an integer array, find a subarray where the sum of numbers is in a given interval. Your code should return the number of possible answers. (The element in the array should be positive) Have you met this question in a real interview? Yes Example Given [1,2,3,4] and interval = [1,3], return 4. The possible answers are: [0, 0] [0, 1] [1, 1] [2, 2]
思路:这里由于是求范围,一边求的前缀和数组prefixSum[i],一边判断前缀和数组中的前面的元素是否有prefixSum[i] - end ~ prefixSum[i] - start这个范围的元素出现,这个判断过程如果直接依次遍历前面的元素是O(n)的,为了加快查找速度,用二分查找,转化为查找prefixSum[i] - end和prefixSum[i] - start + 1(注意:一定要+1,否则会出现错误!!!【0,1,3】查找3~3和3~4的区别)这两个元素的索引,然后这两个索引的差值就是当前满足条件的子数组的个数。遍历整个i即可求的总共的子数组个数。注意这一题中的二分查找的写法,最后返回索引,这个索引是不能随便返回的,按照模版写法最后要进行详细的判断过程。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param A an integer array 4 * @param start an integer 5 * @param end an integer 6 * @return the number of possible answer 7 */ 8 public int subarraySumII(int[] A, int start, int end) { 9 // Write your code here 10 if (A == null || A.length == 0 11 || start <= 0 || end <= 0 || start > end) { 12 return 0; 13 } 14 int n = A.length; 15 int[] prefixSum = new int[n + 1]; 16 int count = 0; 17 for (int i = 1; i <= n; i++) { 18 prefixSum[i] = prefixSum[i - 1] + A[i - 1]; 19 int low = prefixSum[i] - end; 20 int high = prefixSum[i] - start + 1; 21 count += binarySearch(prefixSum, i, high) 22 - binarySearch(prefixSum, i, low); 23 } 24 return count; 25 } 26 public int binarySearch(int[] num, int len, int target) { 27 int start = 0; 28 int end = len - 1; 29 while (start + 1 < end) { 30 int mid = start + (end - start) / 2; 31 if (num[mid] == target) { 32 return mid; 33 } else if (num[mid] < target) { 34 start = mid; 35 } else { 36 end = mid; 37 } 38 } 39 if (num[start] == target) { 40 return start; 41 } 42 if (num[start] > target) { 43 return start; 44 } 45 if (num[end] == target) { 46 return end; 47 } 48 if (num[end] > target) { 49 return end; 50 } 51 return end + 1; 52 } 53 }
Submatrix Sum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an integer matrix, find a submatrix where the sum of numbers is zero. Your code should return the coordinate of the left-up and right-down number. Have you met this question in a real interview? Yes Example Given matrix [ [1 ,5 ,7], [3 ,7 ,-8], [4 ,-8 ,9], ] return [(1,1), (2,2)]
思路:先前缀和预处理,prefixSum[i][j] = sum of submatrix [(0,0), (i - 1,j - 1)].然后对于行数枚举,low:0 ~ n - 1; high:low + 1 ~ n。这样固定了矩阵的宽度,再建立一个map,对高度j进行枚举,只要有两个同高不等宽的子矩阵的和相同,说明这两个子矩阵的非重叠部分的子矩阵的和为0,这样转化为Subarray Sum这道一维的题目。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param matrix an integer matrix 4 * @return the coordinate of the left-up and right-down number 5 */ 6 public int[][] submatrixSum(int[][] matrix) { 7 // Write your code here 8 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 9 return new int[0][0]; 10 } 11 int n = matrix.length; 12 int m = matrix[0].length; 13 int[][] prefixSum = new int[n + 1][m + 1]; 14 for (int i = 1; i <= n; i++) { 15 for (int j = 1; j <= m; j++) { 16 prefixSum[i][j] = prefixSum[i - 1][j] + prefixSum[i][j - 1] - prefixSum[i - 1][j - 1] + matrix[i - 1][j - 1]; 17 } 18 } 19 for (int low = 0; low < n; low++) { 20 for (int high = low + 1; high <= n; high++) { 21 Map<Integer, Integer> map = new HashMap<>(); 22 for (int j = 0; j <= m; j++) { 23 int difference = prefixSum[high][j] - prefixSum[low][j]; 24 if (map.containsKey(difference)) { 25 int[][] result = new int[2][2]; 26 result[0][0] = low; 27 result[0][1] = map.get(difference); 28 result[1][0] = high - 1; 29 result[1][1] = j - 1; 30 return result; 31 } else { 32 map.put(difference, j); 33 } 34 } 35 } 36 } 37 return new int[0][0]; 38 } 39 }
Sliding Window Maximum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of n integer with duplicate number, and a moving window(size k), move the window at each iteration from the start of the array, find the maximum number inside the window at each moving. Have you met this question in a real interview? Yes Example For array [1, 2, 7, 7, 8], moving window size k = 3. return [7, 7, 8] At first the window is at the start of the array like this [|1, 2, 7| ,7, 8] , return the maximum 7; then the window move one step forward. [1, |2, 7 ,7|, 8], return the maximum 7; then the window move one step forward again. [1, 2, |7, 7, 8|], return the maximum 8;
思路:用到双端队列deque在左边删除,右边插入和删除,每次一个元素入列,把列中小于该元素的元素全部出列,列中只保存可能是当前最大值的元素。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param nums: A list of integers. 4 * @return: The maximum number inside the window at each moving. 5 */ 6 public ArrayList<Integer> maxSlidingWindow(int[] nums, int k) { 7 // write your code here 8 ArrayList<Integer> result = new ArrayList<>(); 9 if (nums == null || nums.length == 0 || k <= 0 || k > nums.length) { 10 return result; 11 } 12 Deque<Integer> deque = new ArrayDeque<>(); 13 for (int i = 0; i < k - 1; i++) { 14 inQueue(deque, nums[i]); 15 } 16 for (int i = k - 1; i < nums.length; i++) { 17 inQueue(deque, nums[i]); 18 result.add(deque.peekFirst()); 19 outQueue(deque, nums[i - k + 1]); 20 } 21 return result; 22 } 23 public void inQueue(Deque<Integer> deque, int num) { 24 //不能取等号!当有两个相同最大数字入列时,取等号会把前面一个最大数字 25 //去掉,当outQueue时又会把当前最大数字出列,造成错误。 26 while (!deque.isEmpty() && num > deque.peekLast()) { 27 deque.pollLast(); 28 } 29 deque.offerLast(num); 30 } 31 public void outQueue(Deque<Integer> deque, int num) { 32 if (num == deque.peekFirst()) { 33 deque.pollFirst(); 34 } 35 } 36 }
Sliding Window Matrix Maximum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an array of n * m matrix, and a moving matrix window (size k * k), move the window from top left to botton right at each iteration, find the maximum number inside the window at each moving. Return 0 if the answer does not exist. Have you met this question in a real interview? Yes Example For matrix [ [1, 5, 3], [3, 2, 1], [4, 1, 9], ] The moving window size k = 2. return 13. At first the window is at the start of the array like this [ [|1, 5|, 3], [|3, 2|, 1], [4, 1, 9], ] ,get the sum 11; then the window move one step forward. [ [1, |5, 3|], [3, |2, 1|], [4, 1, 9], ] ,get the sum 11; then the window move one step forward again. [ [1, 5, 3], [|3, 2|, 1], [|4, 1|, 9], ] ,get the sum 10; then the window move one step forward again. [ [1, 5, 3], [3, |2, 1|], [4, |1, 9|], ] ,get the sum 13; SO finally, get the maximum from all the sum which is 13.
思路:先前缀和预处理,prefixSum[i][j] = sum of submatrix [(0,0), (i - 1,j - 1)].然后对于行数和列数枚举,i:0 ~ n - k j : 0 ~ m - k 然后依次遍历每个k * k矩阵,求出每个k * k矩阵的最大值,最后返回这些值中的最大值即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param matrix an integer array of n * m matrix 4 * @param k an integer 5 * @return the maximum number 6 */ 7 public int maxSlidingMatrix(int[][] matrix, int k) { 8 // Write your code here 9 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 10 return 0; 11 } 12 if (k > matrix.length || k > matrix[0].length) { 13 return 0; 14 } 15 int n = matrix.length; 16 int m = matrix[0].length; 17 int[][] prefixSum = new int[n + 1][m + 1]; 18 for (int i = 1; i <= n; i++) { 19 for (int j = 1; j <= m; j++) { 20 prefixSum[i][j] = prefixSum[i - 1][j] + prefixSum[i][j - 1] 21 - prefixSum[i - 1][j - 1] + matrix[i - 1][j - 1]; 22 } 23 } 24 int max = Integer.MIN_VALUE; 25 for (int i = 0; i <= n - k; i++) { 26 for (int j = 0; j <= m - k; j++) { 27 int num = prefixSum[i + k][j + k] - prefixSum[i][j + k] - prefixSum[i + k][j] + prefixSum[i][j]; 28 if (num > max) { 29 max = num; 30 } 31 } 32 } 33 return max; 34 } 35 }
循环连续子序列
Continuous Subarray Sum
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an integer array, find a continuous subarray where the sum of numbers is the biggest. Your code should return the index of the first number and the index of the last number. (If their are duplicate answer, return anyone) Have you met this question in a real interview? Yes Example Give [-3, 1, 3, -3, 4], return [1,4].
思路:求连续子数组最大和/最小和的模版,记下来。是一个叫***算法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param A an integer array 4 * @return A list of integers includes the index of the first number and the index of the last number 5 */ 6 public ArrayList<Integer> continuousSubarraySum(int[] A) { 7 // Write your code here 8 ArrayList<Integer> result = new ArrayList<>(); 9 if (A == null || A.length == 0) { 10 return result; 11 } 12 result.add(0); 13 result.add(0); 14 int start = 0; 15 int end = 0; 16 int sum = 0; 17 int max = Integer.MIN_VALUE; 18 for (int i = 0; i < A.length; i++) { 19 if (sum > 0) { 20 sum += A[i]; 21 end = i; 22 } else { 23 sum = A[i]; 24 start = i; 25 end = i; 26 } 27 if (sum > max) { 28 max = sum; 29 result.set(0, start); 30 result.set(1, end); 31 } 32 } 33 return result; 34 } 35 }
Continuous Subarray Sum II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an circular integer array (the next element of the last element is the first element), find a continuous subarray in it, where the sum of numbers is the biggest. Your code should return the index of the first number and the index of the last number. If duplicate answers exist, return any of them. Have you met this question in a real interview? Yes Example Give [3, 1, -100, -3, 4], return [4,1].
思路:这道题数组变成循环数组了。分两步,第一步跟Continuous Subarray Sum一样求0 ~ len - 1之间的连续子数组的最大和及索引对;第二步考虑尾->首的数组情况,将问题等价于求1 ~ len - 2之间的连续子数组的最小和及索引对,然后用数组和total减去这个最小和就是尾->首的子数组元素和。返回这两步中的最大和及其索引对。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param A an integer array 4 * @return A list of integers includes the index of the first number and the index of the last number 5 */ 6 public ArrayList<Integer> continuousSubarraySumII(int[] A) { 7 // Write your code here 8 ArrayList<Integer> result = new ArrayList<>(); 9 if (A == null || A.length == 0) { 10 return result; 11 } 12 result.add(0); 13 result.add(0); 14 int max = Integer.MIN_VALUE; 15 int total = 0; 16 int sum = 0; 17 int start = 0; 18 int end = 0; 19 for (int i = 0; i < A.length; i++) { 20 total += A[i]; 21 if (sum > 0) { 22 sum += A[i]; 23 end = i; 24 } else { 25 sum = A[i]; 26 start = i; 27 end = i; 28 } 29 if (sum > max) { 30 max = sum; 31 result.set(0, start); 32 result.set(1, end); 33 } 34 } 35 start = 1; 36 end = 1; 37 sum = 0; 38 for (int i = 1; i < A.length - 1; i++) { 39 if (sum < 0) { 40 sum += A[i]; 41 end = i; 42 } else { 43 sum = A[i]; 44 start = i; 45 end = i; 46 } 47 if (total - sum > max) { 48 max = total - sum; 49 result.set(0, end + 1); 50 result.set(1, start - 1); 51 } 52 } 53 return result; 54 } 55 }
Partition Follow UP
Kth Largest问题
PriorityQueue
• 时间复杂度O(nlogk) • 更适合Topk
• QuickSelect
• 时间复杂度O(n) • 更适合第k大
Wiggle Sort
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an unsorted array nums, reorder it in-place such that nums[0] <= nums[1] >= nums[2] <= nums[3].... Notice Please complete the problem in-place. Have you met this question in a real interview? Yes Example Given nums = [3, 5, 2, 1, 6, 4], one possible answer is [1, 6, 2, 5, 3, 4].
思路:很简单。对索引分奇偶考虑,如果索引奇且当前值小于前一个值或者索引偶且当前值大于前一个值,则交换。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param nums a list of integer 4 * @return void 5 */ 6 public void wiggleSort(int[] nums) { 7 // Write your code here 8 if (nums == null || nums.length == 0) { 9 return; 10 } 11 for (int i = 1; i < nums.length; i++) { 12 if (i % 2 == 1 && nums[i] < nums[i - 1] || i % 2 == 0 && nums[i] > nums[i - 1]) { 13 swap(nums, i - 1, i); 14 } 15 } 16 } 17 public void swap(int[] nums, int a, int b) { 18 int temp = nums[a]; 19 nums[a] = nums[b]; 20 nums[b] = temp; 21 } 22 }
Wiggle Sort II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3].... Notice You may assume all input has valid answer. Have you met this question in a real interview? Yes Example Given nums = [1, 5, 1, 1, 6, 4], one possible answer is [1, 4, 1, 5, 1, 6]. Given nums = [1, 3, 2, 2, 3, 1], one possible answer is [2, 3, 1, 3, 1, 2].
思路I:先排序,然后排好序的数组分一半,依次从前一半的末尾和后一半的末尾取数放在原数组中。时间复杂度O(nlgn)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param nums a list of integer 4 * @return void 5 */ 6 public void wiggleSort(int[] nums) { 7 // Write your code here 8 if (nums == null || nums.length == 0) { 9 return; 10 } 11 int[] temp = new int[nums.length]; 12 for (int i = 0; i < nums.length; i++) { 13 temp[i] = nums[i]; 14 } 15 Arrays.sort(temp); 16 int j = nums.length - 1; 17 int k = (nums.length + 1) / 2 - 1; 18 for (int i = 0; i < nums.length; i++) { 19 if (i % 2 == 0) { 20 nums[i] = temp[k--]; 21 } else { 22 nums[i] = temp[j--]; 23 } 24 } 25 } 26 }
思路II:用QuickSelect模版选择第(length + 1) / 2大的数mid,新建temp数组,初始化为mid。然后遍历nums数组,将大于mid的元素从前往后放入temp,将小于mid的元素从后往前(注意初始化的位置由nums数组长度的奇偶来决定是nums.length - 1还是nums.length - 2)放入temp,来分割重复元素,最后将temp中的元素复制到nums中。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param nums a list of integer 4 * @return void 5 */ 6 public void wiggleSort(int[] nums) { 7 // Write your code here 8 if (nums == null || nums.length == 0) { 9 return; 10 } 11 int n = nums.length; 12 int mid = 0; 13 if (n % 2 == 0) { 14 mid = partition(nums, 0, n - 1, n / 2); 15 } else { 16 mid = partition(nums, 0, n - 1, (n + 1) / 2); 17 } 18 int[] temp = new int[n]; 19 for (int i = 0; i < n; i++) { 20 temp[i] = mid; 21 } 22 int l = 1; 23 int r = n - 2; 24 if (n % 2 == 1) { 25 r = n - 1; 26 } 27 for (int i = 0; i < n; i++) { 28 if (nums[i] < mid) { 29 temp[r] = nums[i]; 30 r -= 2; 31 } else if (nums[i] > mid) { 32 temp[l] = nums[i]; 33 l += 2; 34 } 35 } 36 for (int i = 0; i < n; i++) { 37 nums[i] = temp[i]; 38 } 39 } 40 public int partition(int[] nums, int start, int end, int k) { 41 if (start == end) { 42 return nums[start]; 43 } 44 int pivot = nums[start + (end - start) / 2]; 45 int left = start; 46 int right = end; 47 while (left <= right) { 48 while (left <= right && nums[left] < pivot) { 49 left++; 50 } 51 while (left <= right && nums[right] > pivot) { 52 right--; 53 } 54 if (left <= right) { 55 int temp = nums[left]; 56 nums[left] = nums[right]; 57 nums[right] = temp; 58 left++; 59 right--; 60 } 61 } 62 if (k <= right - start + 1) { 63 return partition(nums, start, right, k); 64 } 65 if (k >= left - start + 1) { 66 return partition(nums, left, end, k - (left - start)); 67 } 68 return nums[left - 1]; 69 } 70 }
Nuts & Bolts Problem
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a set of n nuts of different sizes and n bolts of different sizes. There is a one-one mapping between nuts and bolts. Comparison of a nut to another nut or a bolt to another bolt is not allowed. It means nut can only be compared with bolt and bolt can only be compared with nut to see which one is bigger/smaller. We will give you a compare function to compare nut with bolt. Have you met this question in a real interview? Yes Example Given nuts = ['ab','bc','dd','gg'], bolts = ['AB','GG', 'DD', 'BC']. Your code should find the matching bolts and nuts. one of the possible return: nuts = ['ab','bc','dd','gg'], bolts = ['AB','BC','DD','GG']. we will tell you the match compare function. If we give you another compare function. the possible return is the following: nuts = ['ab','bc','dd','gg'], bolts = ['BC','AA','DD','GG']. So you must use the compare function that we give to do the sorting. The order of the nuts or bolts does not matter. You just need to find the matching bolt for each nut.
思路:先从bolts中选一个pivot,对nuts进行partition,然后返回与该pivot对应的nuts中的索引,再以此相对应的pivot对bolts进行partition,这样nuts和bolts就都有一个元素相对应了。然后递归处理左右两边即可。注意这道题由于要nuts的pivot与bolts的pivot的位置对齐,所以不能用令狐冲老师讲的partition模版,这个模版只能将数组分成左右两半部分,但是pivot的索引不定,很可能不会再最终的位置上。所以这里用到另一种确保pivot在最终位置上的partiton写法,记住这个写法,考研的时候学过,具体看程序即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * public class NBCompare { 3 * public int cmp(String a, String b); 4 * } 5 * You can use compare.cmp(a, b) to compare nuts "a" and bolts "b", 6 * if "a" is bigger than "b", it will return 1, else if they are equal, 7 * it will return 0, else if "a" is smaller than "b", it will return -1. 8 * When "a" is not a nut or "b" is not a bolt, it will return 2, which is not valid. 9 */ 10 public class Solution { 11 /** 12 * @param nuts: an array of integers 13 * @param bolts: an array of integers 14 * @param compare: a instance of Comparator 15 * @return: nothing 16 */ 17 public void sortNutsAndBolts(String[] nuts, String[] bolts, NBComparator compare) { 18 // write your code here 19 if (nuts == null || nuts.length == 0 || bolts == null 20 || bolts.length == 0 || nuts.length != bolts.length) { 21 return; 22 } 23 quickSort(nuts, bolts, compare, 0, nuts.length - 1); 24 } 25 public void quickSort(String[] nuts, String[] bolts, NBComparator compare, int start, int end) { 26 if (start >= end) { 27 return; 28 } 29 int index= partition(nuts, bolts[start], compare, start, end); 30 partition(bolts, nuts[index], compare, start, end); 31 quickSort(nuts, bolts, compare, start, index - 1); 32 quickSort(nuts, bolts, compare, index + 1, end); 33 } 34 public int partition(String[] str, String pivot, NBComparator compare, 35 int start, int end) { 36 for (int i = start ; i <= end; i++) { 37 if (compare.cmp(str[i], pivot) == 0 || compare.cmp(pivot, str[i]) == 0) { 38 swap(str, start, i); 39 break; 40 } 41 } 42 String buffer = str[start]; 43 int left = start; 44 int right = end; 45 while (left < right) { 46 while (left < right && (compare.cmp(str[right], pivot) == 1 || compare.cmp(pivot, str[right]) == -1)) { 47 right--; 48 } 49 str[left] = str[right]; 50 while (left < right && (compare.cmp(str[left], pivot) == -1 || compare.cmp(pivot, str[left]) == 1)) { 51 left++; 52 } 53 str[right] = str[left]; 54 } 55 str[left] = buffer; 56 return left; 57 } 58 public void swap(String[] str, int a, int b) { 59 String temp = str[a]; 60 str[a] = str[b]; 61 str[b] = temp; 62 } 63 }
迭代器相关:
Flatten List
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a list, each element in the list can be a list or integer. flatten it into a simply list with integers. Notice If the element in the given list is a list, it can contain list too. Have you met this question in a real interview? Yes Example Given [1,2,[1,2]], return [1,2,1,2]. Given [4,[3,[2,[1]]]], return [4,3,2,1].
思路:遍历每个元素,如果元素是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, 7 * // rather than a nested list. 8 * public boolean isInteger(); 9 * 10 * // @return the single integer that this NestedInteger holds, 11 * // if it holds a single integer 12 * // Return null if this NestedInteger holds a nested list 13 * public Integer getInteger(); 14 * 15 * // @return the nested list that this NestedInteger holds, 16 * // if it holds a nested list 17 * // Return null if this NestedInteger holds a single integer 18 * public List<NestedInteger> getList(); 19 * } 20 */ 21 public class Solution { 22 23 // @param nestedList a list of NestedInteger 24 // @return a list of integer 25 public List<Integer> flatten(List<NestedInteger> nestedList) { 26 // Write your code here 27 List<Integer> result = new ArrayList<>(); 28 for (NestedInteger nest : nestedList) { 29 if (nest.isInteger()) { 30 result.add(nest.getInteger()); 31 } else { 32 result.addAll(flatten(nest.getList())); 33 } 34 } 35 return result; 36 } 37 }
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. Notice You don't need to implement the remove method. Have you met this question in a real interview? Yes Example 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]. 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].
思路:1. List转Stack 2. 主函数放在hasNext()中,next只做一次pop处理。
![](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, 7 * // rather than a nested list. 8 * public boolean isInteger(); 9 * 10 * // @return the single integer that this NestedInteger holds, 11 * // if it holds a single integer 12 * // Return null if this NestedInteger holds a nested list 13 * public Integer getInteger(); 14 * 15 * // @return the nested list that this NestedInteger holds, 16 * // if it holds a nested list 17 * // Return null if this NestedInteger holds a single integer 18 * public List<NestedInteger> getList(); 19 * } 20 */ 21 import java.util.Iterator; 22 23 public class NestedIterator implements Iterator<Integer> { 24 Stack<NestedInteger> stack; 25 public void pushListToStack(List<NestedInteger> nestedList) { 26 Stack<NestedInteger> temp = new Stack<>(); 27 for (NestedInteger nested : nestedList) { 28 temp.push(nested); 29 } 30 while (!temp.isEmpty()) { 31 stack.push(temp.pop()); 32 } 33 } 34 public NestedIterator(List<NestedInteger> nestedList) { 35 // Initialize your data structure here. 36 stack = new Stack<NestedInteger>(); 37 pushListToStack(nestedList); 38 } 39 40 // @return {int} the next element in the iteration 41 @Override 42 public Integer next() { 43 // Write your code here 44 if (!hasNext()) { 45 return null; 46 } 47 return stack.pop().getInteger(); 48 } 49 50 // @return {boolean} true if the iteration has more element or false 51 @Override 52 public boolean hasNext() { 53 // Write your code here 54 while (!stack.isEmpty() && !stack.peek().isInteger()) { 55 pushListToStack(stack.pop().getList()); 56 } 57 return !stack.isEmpty(); 58 } 59 60 @Override 61 public void remove() {} 62 } 63 64 /** 65 * Your NestedIterator object will be instantiated and called as such: 66 * NestedIterator i = new NestedIterator(nestedList); 67 * while (i.hasNext()) v.add(i.next()); 68 */
Flatten 2D Vector
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Implement an iterator to flatten a 2d vector. Have you met this question in a real interview? Yes Example Given 2d vector = [ [1,2], [3], [4,5,6] ] By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,2,3,4,5,6].
思路:同Flatten Nested List Iterator
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Vector2D implements Iterator<Integer> { 2 Stack<List<Integer>> stack1; 3 Stack<Integer> stack2; 4 5 public void pushListListToStack(List<List<Integer>> vec2d) { 6 Stack<List<Integer>> temp = new Stack<>(); 7 for (List<Integer> nested : vec2d) { 8 temp.push(nested); 9 } 10 while (!temp.isEmpty()) { 11 stack1.push(temp.pop()); 12 } 13 } 14 15 public void pushListToStack(List<Integer> vec) { 16 Stack<Integer> temp = new Stack<>(); 17 for (Integer nested : vec) { 18 temp.push(nested); 19 } 20 while (!temp.isEmpty()) { 21 stack2.push(temp.pop()); 22 } 23 } 24 25 public Vector2D(List<List<Integer>> vec2d) { 26 // Initialize your data structure here 27 stack1 = new Stack<>(); 28 pushListListToStack(vec2d); 29 stack2 = new Stack<>(); 30 } 31 32 @Override 33 public Integer next() { 34 // Write your code here 35 if (!hasNext()) { 36 return null; 37 } 38 return stack2.pop(); 39 } 40 41 @Override 42 public boolean hasNext() { 43 // Write your code here 44 while (stack2.isEmpty() && !stack1.isEmpty()) { 45 pushListToStack(stack1.pop()); 46 } 47 return !stack2.isEmpty(); 48 } 49 50 @Override 51 public void remove() {} 52 } 53 54 /** 55 * Your Vector2D object will be instantiated and called as such: 56 * Vector2D i = new Vector2D(vec2d); 57 * while (i.hasNext()) v[f()] = i.next(); 58 */
Binary Search Tree Iterator
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Design an iterator over a binary search tree with the following rules: Elements are visited in ascending order (i.e. an in-order traversal) next() and hasNext() queries run in O(1) time in average. Have you met this question in a real interview? Yes Example For the following binary search tree, in-order traversal by using iterator is [1, 6, 10, 11, 12] 10 / \ 1 11 \ \ 6 12
思路:同Flatten Nested List Iterator。注意当中序非递归把元素压栈之后,要及时把cur指针置为null,否则cur指针还是指向原来的位置而不改变!!!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition of TreeNode: 3 * public class TreeNode { 4 * public int val; 5 * public TreeNode left, right; 6 * public TreeNode(int val) { 7 * this.val = val; 8 * this.left = this.right = null; 9 * } 10 * } 11 * Example of iterate a tree: 12 * BSTIterator iterator = new BSTIterator(root); 13 * while (iterator.hasNext()) { 14 * TreeNode node = iterator.next(); 15 * do something for node 16 * } 17 */ 18 public class BSTIterator { 19 private Stack<TreeNode> stack; 20 private TreeNode cur; 21 22 public void pushNodeToStack(TreeNode root) { 23 while (root != null) { 24 stack.push(root); 25 root = root.left; 26 } 27 } 28 29 //@param root: The root of binary tree. 30 public BSTIterator(TreeNode root) { 31 // write your code here 32 stack = new Stack<TreeNode>(); 33 cur = root; 34 } 35 36 //@return: True if there has next node, or false 37 public boolean hasNext() { 38 // write your code here 39 if (cur != null) { 40 pushNodeToStack(cur); 41 //注意这里一定要将cur置为null 42 cur = null; 43 } 44 return !stack.isEmpty(); 45 } 46 //@return: return next node 47 public TreeNode next() { 48 // write your code here 49 if (!hasNext()) { 50 return null; 51 } 52 TreeNode node = stack.pop(); 53 cur = node.right; 54 return node; 55 } 56 }
Zigzag Iterator
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given two 1d vectors, implement an iterator to return their elements alternately. Have you met this question in a real interview? Yes Example Given two 1d vectors: v1 = [1, 2] v2 = [3, 4, 5, 6] By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1, 3, 2, 4, 5, 6].
思路:用计数器count控制取两个中的哪一个。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class ZigzagIterator { 2 List<Integer> l1; 3 List<Integer> l2; 4 int count; 5 /** 6 * @param v1 v2 two 1d vectors 7 */ 8 public ZigzagIterator(List<Integer> v1, List<Integer> v2) { 9 // initialize your data structure here. 10 l1 = v1; 11 l2 = v2; 12 count = 0; 13 } 14 15 public int next() { 16 // Write your code here 17 if (!hasNext()) { 18 return 0; 19 } 20 int result = 0; 21 if (l1.size() > 0 && l2.size() > 0) { 22 if (count % 2 == 0) { 23 result = l1.remove(0); 24 } else { 25 result = l2.remove(0); 26 } 27 } else if (l1.size() > 0) { 28 result = l1.remove(0); 29 } else { 30 result = l2.remove(0); 31 } 32 count++; 33 return result; 34 } 35 36 public boolean hasNext() { 37 // Write your code here 38 return l1.size() > 0 || l2.size() > 0; 39 } 40 } 41 42 /** 43 * Your ZigzagIterator object will be instantiated and called as such: 44 * ZigzagIterator solution = new ZigzagIterator(v1, v2); 45 * while (solution.hasNext()) result.add(solution.next()); 46 * Output result 47 */
Zigzag Iterator II
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Follow up Zigzag Iterator: What if you are given k 1d vectors? How well can your code be extended to such cases? The "Zigzag" order is not clearly defined and is ambiguous for k > 2 cases. If "Zigzag" does not look right to you, replace "Zigzag" with "Cyclic". Have you met this question in a real interview? Yes Example Given k = 3 1d vectors: [1,2,3] [4,5,6,7] [8,9] Return [1,4,8,2,5,9,3,6,7].
思路:用计数器count控制取k个中的哪一个,当count对应的list没有元素时count++,直到找到含有元素的list。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class ZigzagIterator2 { 2 ArrayList<ArrayList<Integer>> lists; 3 int count; 4 /** 5 * @param vecs a list of 1d vectors 6 */ 7 public ZigzagIterator2(ArrayList<ArrayList<Integer>> vecs) { 8 // initialize your data structure here. 9 lists = vecs; 10 count = 0; 11 } 12 13 public int next() { 14 // Write your code here 15 if (!hasNext()) { 16 return 0; 17 } 18 int result = 0; 19 while (lists.get(count % lists.size()).size() == 0) { 20 count++; 21 } 22 result = lists.get(count % lists.size()).remove(0); 23 count++; 24 return result; 25 } 26 27 public boolean hasNext() { 28 // Write your code here 29 if (lists == null || lists.size() == 0) { 30 return false; 31 } 32 for (int i = 0; i < lists.size(); i++) { 33 if (lists.get(i) != null && lists.get(i).size() != 0) { 34 return true; 35 } 36 } 37 return false; 38 } 39 } 40 41 /** 42 * Your ZigzagIterator2 object will be instantiated and called as such: 43 * ZigzagIterator2 solution = new ZigzagIterator2(vecs); 44 * while (solution.hasNext()) result.add(solution.next()); 45 * Output result 46 */
Maximum Gap
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given an unsorted array, find the maximum difference between the successive elements in its sorted form. Return 0 if the array contains less than 2 elements. Notice You may assume all elements in the array are non-negative integers and fit in the 32-bit signed integer range. Have you met this question in a real interview? Yes Example Given [1, 9, 2, 5], the sorted form of it is [1, 2, 5, 9], the maximum gap is between 5 and 9 = 4.
思路:桶排序(bucket sort)
假设有N个元素A到B。
那么最大差值不会小于ceiling[(B - A) / (N - 1)] (ceiling为向上取整)
令bucket(桶)的大小len = ceiling[(B - A) / (N - 1)],则最多会有(B - A) / len + 1个桶
对于数组中的任意整数K,很容易通过算式loc = (K - A) / len找出其桶的位置,然后维护每一个桶的最大值和最小值
由于同一个桶内的元素之间的差值至多为len - 1,因此最终答案不会从同一个桶中选择。
对于每一个非空的桶p,找出下一个非空的桶q,则q.min - p.max可能就是备选答案。返回所有这些可能值中的最大值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Solution { 2 /** 3 * @param nums: an array of integers 4 * @return: the maximum difference 5 */ 6 public int maximumGap(int[] nums) { 7 // write your code here 8 if (nums == null || nums.length < 2) { 9 return 0; 10 } 11 int max = -1; 12 int min = Integer.MAX_VALUE; 13 for (int i = 0; i < nums.length; i++) { 14 if (nums[i] > max) { 15 max = nums[i]; 16 } 17 if (nums[i] < min) { 18 min = nums[i]; 19 } 20 } 21 int capacity = Math.max(1, (max - min - 1) / (nums.length - 1) + 1); 22 int length = (max - min) / capacity + 1; 23 int[] localMin = new int[length]; 24 int[] localMax = new int[length]; 25 for (int i = 0; i < length; i++) { 26 localMin[i] = Integer.MAX_VALUE; 27 localMax[i] = -1; 28 } 29 for (int i = 0; i < nums.length; i++) { 30 int index = (nums[i] - min) / capacity; 31 localMin[index] = Math.min(localMin[index], nums[i]); 32 localMax[index] = Math.max(localMax[index], nums[i]); 33 } 34 int upMax = localMax[0]; 35 int result = 0; 36 for (int i = 1; i < length; i++) { 37 if (localMax[i] == -1) { 38 continue; 39 } 40 result = Math.max(result, localMin[i] - upMax); 41 upMax = localMax[i]; 42 } 43 return result; 44 } 45 }
Bomb Enemy
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Given a 2D grid, each cell is either a wall 'W', an enemy 'E' or empty '0' (the number zero), return the maximum enemies you can kill using one bomb.
The bomb kills all the enemies in the same row and column from the planted point until it hits the wall since the wall is too strong to be destroyed.
注意事项
You can only put the bomb at an empty cell.
您在真实的面试中是否遇到过这个题? Yes
样例
Given a grid:
0 E 0 0
E 0 W E
0 E 0 0
return 3. (Placing a bomb at (1,1) kills 3 enemies)
思路:扫描矩阵,用rows表示该点所在行内有多少敌人可见。用cols[i]表示该点所在列i内有多少敌人可见。然后该点可见的敌人为两者之和。
初始时两值为0.
当我们位于行首或者该行上一格为墙,那么在该行向右扫描直到行尾或者遇见一堵墙,更新rows。
当我们位于列首或者该列上一格为墙,那么在该列向下扫描直到列尾或者遇见一堵墙,更新cols[i]。
时间复杂度O(mn)(这个复杂度我还没想明白),空间复杂度O(N)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 /** 3 * @param grid Given a 2D grid, each cell is either 'W', 'E' or '0' 4 * @return an integer, the maximum enemies you can kill using one bomb 5 */ 6 public int maxKilledEnemies(char[][] grid) { 7 // Write your code here 8 if (grid == null || grid.length == 0 || grid[0].length == 0) { 9 return 0; 10 } 11 int n = grid.length; 12 int m = grid[0].length; 13 int result = 0; 14 int rows = 0; 15 int[] cols = new int[m]; 16 for (int i = 0; i < n; i++) { 17 for (int j = 0; j < m; j++) { 18 if (j == 0 || grid[i][j - 1] == 'W') { 19 rows = 0; 20 for (int k = j; k < m && grid[i][k] != 'W'; k++) { 21 if (grid[i][k] == 'E') { 22 rows++; 23 } 24 } 25 } 26 if (i == 0 || grid[i - 1][j] == 'W') { 27 cols[j] = 0; 28 for (int k = i; k < n && grid[k][j] != 'W'; k++) { 29 if (grid[k][j] == 'E') { 30 cols[j]++; 31 } 32 } 33 } 34 if (grid[i][j] == '0' && rows + cols[j] > result) { 35 result = rows + cols[j]; 36 } 37 } 38 } 39 return result; 40 } 41 }