第一節:follow up問題html
1.兩數之和 II node
--給一組整數,問能找出多少對整數,他們的和大於一個給定的目標值。c++
解析:與two sum相似,先排序,再兩指針。面試
1 class Solution { 2 public: 3 /** 4 * @param nums: an array of integer 5 * @param target: an integer 6 * @return: an integer 7 */ 8 int twoSum2(vector<int> &nums, int target) { 9 // Write your code here 10 sort(nums.begin(), nums.end()); 11 int left = 0; 12 int right = nums.size() - 1; 13 int ans = 0; 14 while (left < right) 15 { 16 if (nums[left] + nums[right] > target) 17 { 18 ans += right - left; 19 right--; 20 } else 21 { 22 left++; 23 } 24 } 25 return ans; 26 } 27 };
2.三角形計數 數組
--給定一個整數數組,在該數組中,尋找三個數,分別表明三角形三條邊的長度,問,能夠尋找到多少組這樣的三個數來組成三角形?安全
解析:two sumII的follow up ,卡在怎麼同時證實三個數任意兩個之和大於第三個數,其實數組排序了以後,只要證實兩個小的數之和大於大的數就能夠了。數據結構
1 class Solution { 2 public: 3 /** 4 * @param S: A list of integers 5 * @return: An integer 6 */ 7 int triangleCount(vector<int> &S) { 8 // write your code here 9 sort(S.begin(), S.end()); 10 int ans = 0; 11 for (int i = 2; i < S.size(); i++) 12 { 13 int left = 0; 14 int right = i - 1; 15 while (left < right) 16 { 17 if (S[left] + S[right] > S[i]) 18 { 19 ans += right - left; 20 right--; 21 } else 22 { 23 left++; 24 } 25 } 26 } 27 return ans; 28 } 29 };
--在一個排序矩陣中找從小到大的第 k 個整數。排序矩陣的定義爲:每一行遞增,每一列也遞增。app
解析:優先級隊列往右和下兩個方向遍歷,記住兩個方向遍歷的小技巧,還有上下左右四個方向遍歷的技巧,int posX = {0,1,0,-1};int posY = {-1,0,1,0};ide
1 public class Solution { 2 /** 3 * @param matrix: a matrix of integers 4 * @param k: an integer 5 * @return: the kth smallest number in the matrix 6 */ 7 public int kthSmallest(int[][] matrix, int k) { 8 // write your code here 9 if (matrix == null || k < 1) { 10 return 0; 11 } 12 int m = matrix.length; 13 int n = matrix[0].length; 14 if (m == 0 || n == 0) { 15 return 0; 16 } 17 boolean[][] visited = new boolean[m][n]; 18 PriorityQueue<Element> pq = new PriorityQueue<>(k, new MyComparator()); 19 pq.offer(new Element(0, 0, matrix[0][0])); 20 visited[0][0] = true; 21 //向右和向下兩個方向遍歷 22 int[] posX = {0,1}; 23 int[] posY = {1,0}; 24 25 for (int i = 0; i < k - 1; i++) { 26 Element cur = pq.poll(); 27 for (int j = 0; j < 2; j++) { 28 int x_next = cur.x + posX[j]; 29 int y_next = cur.y + posY[j]; 30 if (x_next < m && y_next < n && !visited[x_next][y_next]) { 31 pq.offer(new Element(x_next, y_next, matrix[x_next][y_next])); 32 visited[x_next][y_next] = true; 33 } 34 } 35 } 36 return pq.poll().val; 37 } 38 public class Element { 39 int x = 0; 40 int y = 0; 41 int val = 0; 42 public Element(int x, int y, int val) { 43 this.x = x; 44 this.y = y; 45 this.val = val; 46 } 47 } 48 public class MyComparator implements Comparator<Element> { 49 public int compare(Element e1, Element e2) { 50 return e1.val - e2.val; 51 } 52 } 53 }
4.排序矩陣中的從小到大第k個數follow up ---兩個排序數組和的第k小函數
--給定兩個排好序的數組 A, B,定義集合 sum = a + b ,求 sum 中第k小的元素。
解析:這題跟第3題實際上是同樣的,給出 A = [1,7,11]
B = [2,4,6],能夠得出和的表格
A\B | 2 | 4 | 6 |
1 | 3 | 5 | 7 |
7 | 9 | 11 | 13 |
11 | 13 | 16 | 17 |
其實和的矩陣就是第三題的行列遞增的矩陣,因此解法與第三題是相同的。
第二節-- 高級數據結構
1.並查集
並查集的兩個基本操做,查詢與合併。
查詢--找到這個節點的最終父節點,使用帶壓縮路徑的遞歸,能夠作到O(1)平均時間複雜度。
合併--合併兩個集合,方法是將兩個集合的根結點鏈接起來,O(1)平均時間複雜度。
並查集原生題
a.connecting graph
題意:
Given n nodes in a graph labeled from 1 to n. There is no edges in the graph at beginning. You need to support the following method: 1. connect(a, b), add an edge to connect node a and node b. 2.query(a, b)`, check if two nodes are connected 樣例 5 // n = 5 query(1, 2) return false connect(1, 2) query(1, 3) return false connect(2, 4) query(1, 4) return true
解法:
1 public class ConnectingGraph { 2 int[] father; 3 public ConnectingGraph(int n) { 4 // initialize your data structure here. 5 father = new int[n + 1]; 6 for (int i = 1; i <= n; i++) { 7 father[i] = i; 8 } 9 } 10 11 public void connect(int a, int b) { 12 // Write your code here 13 int fa = find(a); 14 int fb = find(b); 15 if (fa != fb) { 16 father[fa] = fb; 17 } 18 } 19 20 public boolean query(int a, int b) { 21 // Write your code here 22 if (find(a) == find(b)) { 23 return true; 24 } 25 return false; 26 } 27 public int find(int a) { 28 // Write your code here 29 if (father[a] == a) { 30 return a; 31 } 32 return father[a] = find(father[a]); 33 } 34 }
b.connecting graph II
題意:
Given n nodes in a graph labeled from 1 to n. There is no edges in the graph at beginning. You need to support the following method: 1. connect(a, b), an edge to connect node a and node b 2. query(a), Returns the number of connected component nodes which include node a. 樣例 5 // n = 5 query(1) return 1 connect(1, 2) query(1) return 2 connect(2, 4) query(1) return 3 connect(1, 4) query(1) return 3
解法:
1 public class ConnectingGraph2 { 2 3 int[] father; 4 int[] size; 5 public ConnectingGraph2(int n) { 6 // initialize your data structure here. 7 father = new int[n + 1]; 8 size = new int[n + 1]; 9 for (int i = 1; i <= n; i++) { 10 father[i] = i; 11 size[i] = 1; 12 } 13 } 14 15 public void connect(int a, int b) { 16 // Write your code here 17 int fa = find(a); 18 int fb = find(b); 19 if (fa != fb) { 20 father[fa] = fb; 21 size[fb] += size[fa]; 22 } 23 } 24 25 public int query(int a) { 26 // Write your code here 27 return size[find(a)]; 28 } 29 public int find(int a) { 30 if (father[a] == a) { 31 return a; 32 } 33 return father[a] = find(father[a]); 34 } 35 }
c.connecting graph III
題意:
Given n nodes in a graph labeled from 1 to n. There is no edges in the graph at beginning. You need to support the following method: 1. connect(a, b), an edge to connect node a and node b 2. query(), Returns the number of connected component in the graph 樣例 5 // n = 5 query() return 5 connect(1, 2) query() return 4 connect(2, 4) query() return 3 connect(1, 4) query() return 3
解法:
1 public class ConnectingGraph3 { 2 3 int[] father; 4 int count; 5 public ConnectingGraph3(int n) { 6 father = new int[n + 1]; 7 count = n; 8 for (int i = 1; i <= n; i++) { 9 father[i] = i; 10 } 11 } 12 13 public void connect(int a, int b) { 14 int root_a = find(a); 15 int root_b = find(b); 16 if (root_a != root_b) { 17 father[root_a] = root_b; 18 count--; 19 } 20 } 21 22 public int query() { 23 return count; 24 } 25 public int find(int x) { 26 if (father[x] == x) { 27 return x; 28 } 29 return father[x] = find(father[x]); 30 } 31 }
衍生題--Number of Islands II
題意:
給定 n,m,分別表明一個2D矩陣的行數和列數,同時,給定一個大小爲 k 的二元數組A。起初,2D矩陣的行數和列數均爲 0,即該矩陣中只有海洋。二元數組有 k 個運算符,每一個運算符有 2 個整數 A[i].x, A[i].y,你可經過改變矩陣網格中的[A[i].x,[A[i].y] 來將其由海洋改成島嶼。請在每次運算後,返回矩陣中島嶼的數量。 注意事項 0 表明海,1 表明島。若是兩個1相鄰,那麼這兩個1屬於同一個島。咱們只考慮上下左右爲相鄰。 樣例 給定 n = 3, m = 3, 二元數組 A = [(0,0),(0,1),(2,2),(2,1)]. 返回 [1,1,2,2].
解法:其實就是求集合的個數,而後在新的島嶼生成後,將上下左右的集合合併就好。
1 /** 2 * Definition for a point. 3 * class Point { 4 * int x; 5 * int y; 6 * Point() { x = 0; y = 0; } 7 * Point(int a, int b) { x = a; y = b; } 8 * } 9 */ 10 public class Solution { 11 /** 12 * @param n an integer 13 * @param m an integer 14 * @param operators an array of point 15 * @return an integer array 16 */ 17 private int[] father; 18 public List<Integer> numIslands2(int n, int m, Point[] operators) { 19 // Write your code here 20 if (n == 0 || m == 0 || operators == null || operators.length == 0) { 21 return new ArrayList<>(); 22 } 23 father = new int[n * m]; 24 for (int i = 0; i < n * m; i++) { 25 father[i] = -1; 26 } 27 int num = 0; 28 int len = operators.length; 29 int[] posX = {0, 1, -1, 0}; 30 int[] posY = {1, 0, 0, -1}; 31 List<Integer> ans = new ArrayList<>(); 32 for (int i = 0; i < len; i++) { 33 int x = operators[i].x; 34 int y = operators[i].y; 35 if (father[x * m + y] != -1) { 36 ans.add(num); 37 continue; 38 } 39 father[x * m + y] = x * m + y; 40 num++; 41 for (int j = 0; j < 4; j++) { 42 int nX = x + posX[j]; 43 int nY = y + posY[j]; 44 if (nX >= 0 && nX < n && nY >= 0 && nY < m && father[nX * m + nY] != -1) { 45 num = union(x * m + y, nX * m + nY, num); 46 } 47 } 48 ans.add(num); 49 } 50 return ans; 51 } 52 public int find(int a) { 53 if (father[a] == a) { 54 return a; 55 } 56 return father[a] = find(father[a]); 57 } 58 public int union(int a, int b, int num) { 59 int fa = find(a); 60 int fb = find(b); 61 if (fa != fb) { 62 father[fa] = fb; 63 num--; 64 } 65 return num; 66 } 67 }
判斷圖是不是樹
題意:
給出 n 個節點,標號分別從 0 到 n - 1 而且給出一個 無向 邊的列表 (給出每條邊的兩個頂點), 寫一個函數去判斷這張`無向`圖是不是一棵樹 注意事項 你能夠假設咱們不會給出重複的邊在邊的列表當中. 無向邊 [0, 1] 和 [1, 0] 是同一條邊, 所以他們不會同時出如今咱們給你的邊的列表當中。 樣例 給出n = 5 而且 edges = [[0, 1], [0, 2], [0, 3], [1, 4]], 返回 true. 給出n = 5 而且 edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]], 返回 false.
--圖是連通圖,而且不存在環。那麼這個圖是樹
解法:並查集。並查集用來判斷是否存在環,同時記下連通塊的個數,最後只剩一個連通塊而且沒有環,則就是樹。
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 int[] father = null; 8 public boolean validTree(int n, int[][] edges) { 9 // Write your code here 10 if (edges == null || n == 0) { 11 return false; 12 } 13 father = new int[n]; 14 for (int i = 0; i < n; i++) { 15 father[i] = i; 16 } 17 int count = n; 18 for (int i = 0; i < edges.length; i++) { 19 int fa = find(edges[i][0]); 20 int fb = find(edges[i][1]); 21 //fa == fb存在環 22 if (fa == fb) { 23 return false; 24 } 25 father[fa] = fb; 26 count--; 27 } 28 //count==1說明是連通 29 return count == 1; 30 } 31 public int find(int x) { 32 if (father[x] == x) { 33 return x; 34 } 35 return father[x] = find(father[x]); 36 } 37 }
2.trie tree
1.Implement trie tree
--實現trie tree
解法:這裏用hashmap+非遞歸的方法,其餘實現見http://www.cnblogs.com/fisherinbox/p/6073183.html
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 // Initialize your data structure here. 10 HashMap<Character,TrieNode> children = null; 11 boolean wordEnd; 12 public TrieNode() { 13 children = new HashMap<>(); 14 wordEnd = false; 15 } 16 } 17 18 public class Trie { 19 private TrieNode root; 20 21 public Trie() { 22 root = new TrieNode(); 23 } 24 25 // Inserts a word into the trie. 26 public void insert(String word) { 27 if (word == null || word.length() == 0) { 28 return; 29 } 30 TrieNode cur = root; 31 for (int i = 0; i < word.length(); i++) { 32 TrieNode next = null; 33 if (cur.children.containsKey(word.charAt(i))) { 34 next = cur.children.get(word.charAt(i)); 35 } else { 36 next = new TrieNode(); 37 cur.children.put(word.charAt(i), next); 38 } 39 if (i == word.length() - 1) { 40 next.wordEnd = true; 41 } 42 cur = next; 43 } 44 } 45 46 // Returns if the word is in the trie. 47 public boolean search(String word) { 48 if (word == null || word.length() == 0) { 49 return false; 50 } 51 TrieNode cur = root; 52 for (int i = 0; i < word.length(); i++) { 53 if (!cur.children.containsKey(word.charAt(i))) { 54 return false; 55 } 56 if (i == word.length() - 1 && cur.children.get(word.charAt(i)).wordEnd) { 57 return true; 58 } 59 cur = cur.children.get(word.charAt(i)); 60 } 61 return false; 62 } 63 64 // Returns if there is any word in the trie 65 // that starts with the given prefix. 66 public boolean startsWith(String prefix) { 67 if (prefix == null || prefix.length() == 0) { 68 return false; 69 } 70 TrieNode cur = root; 71 for (int i = 0; i < prefix.length(); i++) { 72 if (!cur.children.containsKey(prefix.charAt(i))) { 73 return false; 74 } 75 cur = cur.children.get(prefix.charAt(i)); 76 } 77 return true; 78 } 79 }
2.單詞搜索II
題意:
給出一個由小寫字母組成的矩陣和一個字典。找出全部同時在字典和矩陣中出現的單詞。一個單詞能夠從矩陣中的任意位置開始,能夠向左/右/上/下四個相鄰方向移動。 您在真實的面試中是否遇到過這個題? Yes 樣例 給出矩陣: doaf agai dcan 和字典: {"dog", "dad", "dgdg", "can", "again"} 返回 {"dog", "dad", "can", "again"}
解法:利用trie 樹+dfs,先將字典中的單詞插入到trie tree中,再dfs遍歷矩陣,這樣的好處是避免無效的遍歷,利用trie tree能夠判斷當前位置的字符是否在trie tree的當前節點的孩子節點中,若是在就往下遍歷,不在就無需遍歷了。輸出結果不能有重複的單詞。
1 public class Solution { 2 /** 3 * @param board: A list of lists of character 4 * @param words: A list of string 5 * @return: A list of string 6 */ 7 public ArrayList<String> wordSearchII(char[][] board, ArrayList<String> words) { 8 // write your code here 9 if (board == null || words == null) { 10 return new ArrayList<>(); 11 } 12 TrieTree trie = new TrieTree(); 13 for (String word : words) { 14 trie.insert(word); 15 } 16 Set<String> set = new HashSet<>(); 17 ArrayList<String> ans = new ArrayList<>(); 18 StringBuffer sb = new StringBuffer(); 19 for (int i = 0; i < board.length; i++) { 20 for (int j = 0; j < board[0].length; j++) { 21 if (trie.root.children.containsKey(board[i][j])) { 22 helper(board, i, j, trie.root, set, sb); 23 } 24 } 25 } 26 for (String word : set) { 27 ans.add(word); 28 } 29 return ans; 30 } 31 public void helper(char[][] board, int i, int j, TrieNode cur, Set<String> ans, StringBuffer sb) { 32 if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] == '#') { 33 return; 34 } 35 char curChar = board[i][j]; 36 board[i][j] = '#'; 37 if (cur.children.containsKey(curChar)) { 38 sb.append(curChar); 39 if (cur.children.get(curChar).wordEnd) { 40 ans.add(new String(sb)); 41 } 42 cur = cur.children.get(curChar); 43 helper(board, i + 1, j, cur, ans, sb); 44 helper(board, i - 1, j, cur, ans, sb); 45 helper(board, i, j - 1, cur, ans, sb); 46 helper(board, i, j + 1, cur, ans, sb); 47 sb.deleteCharAt(sb.length() - 1); 48 } 49 board[i][j] = curChar; 50 } 51 } 52 class TrieNode { 53 HashMap<Character, TrieNode> children = null; 54 boolean wordEnd; 55 public TrieNode() { 56 children = new HashMap<>(); 57 wordEnd = false; 58 } 59 } 60 class TrieTree { 61 TrieNode root = null; 62 public TrieTree() { 63 root = new TrieNode(); 64 } 65 public void insert(String word) { 66 if (word == null || word.length() == 0) { 67 return ; 68 } 69 TrieNode cur = root; 70 for (int i = 0; i < word.length(); i++) { 71 if (!cur.children.containsKey(word.charAt(i))) { 72 cur.children.put(word.charAt(i), new TrieNode()); 73 } 74 cur = cur.children.get(word.charAt(i)); 75 if (i == word.length() - 1) { 76 cur.wordEnd = true; 77 } 78 } 79 } 80 }
第三節 高級數據結構II
1.Trapping Rain Water
題意:
Given n non-negative integers representing an elevation map where the width of each bar is 1
, compute how much water it is able to trap after raining.
Given [0,1,0,2,1,0,1,3,2,1,2,1]
, return 6
.
解法:兩指針解法,分別初始化位頭和尾,始終移動高度較小的一端,而且在移動的同時,計算儲水量。較小的高度是儲水量的上界(木桶原理相似),在移動的同時保存一個儲水上界,遇到較低的高度則計算高度差則是儲水量,遇到較高的高度則更新儲水上界爲這個高度。
1 public class Solution { 2 /** 3 * @param heights: an array of integers 4 * @return: a integer 5 */ 6 public int trapRainWater(int[] heights) { 7 // write your code here 8 if (heights == null || heights.length < 3) { 9 return 0; 10 } 11 int left = 0; 12 int right = heights.length - 1; 13 int cur = Math.min(heights[left], heights[right]); 14 int ans = 0; 15 while(left < right) { 16 if (heights[left] <= heights[right]) { 17 ans += Math.max(cur - heights[left], 0); 18 cur = Math.max(cur, heights[left]); 19 left++; 20 } else { 21 ans += Math.max(cur - heights[right], 0); 22 cur = Math.max(cur, heights[right]); 23 right--; 24 } 25 } 26 return ans; 27 } 28 }
2. Trapping Rain Water II--pq
題意:
Given n x m non-negative integers representing an elevation map 2d where the area of each cell is 1 x 1, compute how much water it is able to trap after raining.
例如,給定一個 5*4 的矩陣:
[ [12,13,0,12], [13,4,13,12], [13,8,10,12], [12,13,12,12], [13,13,13,13] ]
返回 14
.
解法:pq ,與上一題不一樣,這題立體化了。咱們須要由外向內遍歷,而且使用pq來給柱子高度排序,以最小高度向內灌水,遇到高度比當前最小高度小的,則將當前高度替換實際高度加入pq中。注意pq中存的是自定義類,由於要保存位置信息。能夠將已經加入pq中的單元格值至爲-1,用來去重,而且能夠保證是由外向內遍歷的。
1 public class Solution { 2 /** 3 * @param heights: a matrix of integers 4 * @return: an integer 5 */ 6 public int trapRainWater(int[][] heights) { 7 // write your code here 8 if (heights == null || heights.length < 3 || heights[0].length < 3) { 9 return 0; 10 } 11 int m = heights.length; 12 int n = heights[0].length; 13 PriorityQueue<Cell> pq = new PriorityQueue<Cell>(2*(m+n),new MyComparator()); 14 for (int i = 0; i < n; i++) { 15 pq.offer(new Cell(0, i, heights[0][i])); 16 pq.offer(new Cell(m - 1, i, heights[m - 1][i])); 17 heights[0][i] = -1; 18 heights[m - 1][i] = -1; 19 } 20 for (int i = 1; i < m - 1; i++) { 21 pq.offer(new Cell(i, 0, heights[i][0])); 22 pq.offer(new Cell(i, n - 1, heights[i][n - 1])); 23 heights[i][0] = -1; 24 heights[i][n - 1] = -1; 25 } 26 int[] dx = {0, 1, -1, 0}; 27 int[] dy = {1, 0, 0, -1}; 28 int ans = 0; 29 while (!pq.isEmpty()) { 30 Cell cur = pq.poll(); 31 for (int i = 0; i < 4; i++) { 32 int nx = cur.x + dx[i]; 33 int ny = cur.y + dy[i]; 34 if (nx >= 0 && nx < m && ny >= 0 && ny < n && heights[nx][ny] != -1) { 35 ans += Math.max(0, cur.val - heights[nx][ny]); 36 pq.offer(new Cell(nx, ny, Math.max(cur.val, heights[nx][ny]))); 37 heights[nx][ny] = -1; 38 } 39 } 40 } 41 return ans; 42 } 43 public class Cell{ 44 int x; 45 int y; 46 int val; 47 public Cell(int x, int y, int val) { 48 this.x = x; 49 this.y = y; 50 this.val = val; 51 } 52 } 53 public class MyComparator implements Comparator<Cell> { 54 public int compare(Cell c1, Cell c2) { 55 return c1.val - c2.val; 56 } 57 } 58 };
3.實時數據流的中位數
思路:用一個最大堆和一個最小堆。將數據流平均分紅兩個部分,最大堆中的數都小於最小堆中的數,當前數據流個數是偶數的時候,中位數則是最大堆與最小堆堆頂元素之和/2;當前數據流個數是奇數的時候,中位數是最大堆的堆頂。因此當數據流個數是偶數時,最大堆與最小堆數據個數相等,反之,最大堆的數據個數比最小堆多一個。
1.當最大堆與最小堆長度相等時,新來一個數據,本應該加入最大堆,可是若是這個數據比最小堆的最小值要大,就要加入最小堆,再將最小堆的堆頂元素pop出來加入最大堆中。
2.當最大堆長度大於最小堆時,新來一個數據,本應該加入最小堆,可是若是這個數據比最大堆最大值要小,則應該加入最大堆,而且將最大堆的最大值pop加入最小堆。
經過上面兩步保證最大堆的全部元素小於最小堆。
插入時間複雜度爲O(logn),獲取中位數的時間複雜度爲O(1);
代碼使用c++寫的,注意priority_queue的pop函數返回void,並不會返回數值。最小堆的實現沒有自定義比較器,而是將數值取負號放入最大堆,至關於造成了一個最小堆。這時候用long類型就比較安全,由於最小負整數加負號以後會越界int的。
1 class MedianFinder { 2 priority_queue<long> small, large; 3 public: 4 /** initialize your data structure here. */ 5 MedianFinder() { 6 7 } 8 9 void addNum(int num) { 10 if (small.size() == large.size()) { 11 if (large.size() != 0 && num > -large.top()) { 12 large.push(-num); 13 small.push(-large.top()); 14 large.pop(); 15 } else { 16 small.push(num); 17 } 18 } 19 if (small.size() > large.size()) { 20 if (num < small.top()) { 21 small.push(num); 22 large.push(- small.top()); 23 small.pop(); 24 } else { 25 large.push(- num); 26 } 27 } 28 } 29 30 double findMedian() { 31 return small.size() > large.size() ? small.top() : (small.top() - large.top()) / 2.0; 32 } 33 }; 34 35 /** 36 * Your MedianFinder object will be instantiated and called as such: 37 * MedianFinder obj = new MedianFinder(); 38 * obj.addNum(num); 39 * double param_2 = obj.findMedian(); 40 */
4.滑動窗口的中位數
題意:
給定一個包含 n 個整數的數組,和一個大小爲 k 的滑動窗口,從左到右在數組中滑動這個窗口,找到數組中每一個窗口內的中位數。(若是數組個數是偶數,則在該窗口排序數字後,返回第 N/2 個數字。) 樣例 對於數組 [1,2,7,8,5], 滑動大小 k = 3 的窗口時,返回 [2,7,7] 最初,窗口的數組是這樣的: [ | 1,2,7 | ,8,5] , 返回中位數 2; 接着,窗口繼續向前滑動一次。 [1, | 2,7,8 | ,5], 返回中位數 7; 接着,窗口繼續向前滑動一次。 [1,2, | 7,8,5 | ], 返回中位數 7;
解法:與上一題相似,利用兩個堆,可是這裏是滑動窗口,因此每一步須要刪除掉出窗口的元素,而pq的刪除操做時間複雜度是O(n),不適合,這裏用treeset,內部結構式平衡二叉搜索樹,刪除操做時間複雜度是O(logn).
1 public class Solution { 2 /** 3 * @param nums: A list of integers. 4 * @return: The median of the element inside the window at each moving. 5 */ 6 public ArrayList<Integer> medianSlidingWindow(int[] nums, int k) { 7 // write your code here 8 if (nums == null || nums.length == 0 || k <= 0) { 9 return new ArrayList<Integer>(); 10 } 11 ArrayList<Integer> ans = new ArrayList<>(); 12 TreeSet<Node> minHeap = new TreeSet<>(); 13 TreeSet<Node> maxHeap = new TreeSet<>(); 14 int size = (k + 1) / 2; 15 for (int i = 0; i < k - 1; i++) { 16 add(minHeap, maxHeap, size, new Node(i, nums[i])); 17 } 18 for (int i = k - 1; i < nums.length; i++) { 19 add(minHeap, maxHeap, size, new Node(i, nums[i])); 20 ans.add(maxHeap.last().val); 21 remove(minHeap, maxHeap, new Node(i - k + 1, nums[i - k + 1])); 22 } 23 return ans; 24 } 25 public void add(TreeSet<Node> minHeap, TreeSet<Node> maxHeap, int size, Node node) { 26 if (maxHeap.size() < size) { 27 maxHeap.add(node); 28 } else { 29 minHeap.add(node); 30 } 31 if (maxHeap.size() == size) { 32 if (minHeap.size() > 0 && maxHeap.last().val > minHeap.first().val) { 33 Node min = minHeap.first(); 34 Node max = maxHeap.last(); 35 minHeap.remove(min); 36 maxHeap.remove(max); 37 minHeap.add(max); 38 maxHeap.add(min); 39 } 40 } 41 } 42 public void remove(TreeSet<Node> minHeap, TreeSet<Node> maxHeap, Node node) { 43 if (minHeap.contains(node)) { 44 minHeap.remove(node); 45 } else { 46 maxHeap.remove(node); 47 } 48 } 49 public class Node implements Comparable<Node>{ 50 int pos = 0; 51 int val = 0; 52 public Node (int pos, int val) { 53 this.pos = pos; 54 this.val = val; 55 } 56 public int compareTo(Node other) { 57 if (this.val == other.val) { 58 return this.pos - other.pos; 59 } 60 return this.val - other.val; 61 } 62 } 63 }
5.滑動窗口的最大值
思路:利用雙端隊列。隊列頭部始終保存當前滑動窗口的最大值。遍歷數組,若是當前數值大於隊列尾部數值,則刪除全部小於當前數字的隊列尾部數字,並在尾部加入當前數值,若是當前數值小於隊列尾部,則加入尾部。若是當前索引號與隊列頭部索引號之差大於等於滑動窗口大小k,則刪除頭部,由於頭部已經不屬於當前滑動窗口。注意隊列中存的是數值的索引號。
1 class Solution { 2 public: 3 vector<int> maxSlidingWindow(vector<int>& nums, int k) { 4 vector<int> max; 5 if (nums.size() < k || k < 1) { 6 return max; 7 } 8 deque<int> dq; 9 for (int i = 0; i < k; i++) { 10 while (!dq.empty() && nums[i] > nums[dq.back()]) { 11 dq.pop_back(); 12 } 13 dq.push_back(i); 14 } 15 max.push_back(nums[dq.front()]); 16 for (int i = k; i < nums.size(); i++) { 17 while (!dq.empty() && nums[i] >= nums[dq.back()]) { 18 dq.pop_back(); 19 } 20 if (!dq.empty() && dq.front() <= i - k) { 21 dq.pop_front(); 22 } 23 dq.push_back(i); 24 max.push_back(nums[dq.front()]); 25 } 26 return max; 27 } 28 };