九章算法强化

第一节:follow up问题

1.两数之和 II

--给一组整数,问能找出多少对整数,他们的和大于一个给定的目标值。

解析:与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 };
View Code

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 };
View Code

3.排序矩阵中的从小到大第k个数

--在一个排序矩阵中找从小到大的第 k 个整数。排序矩阵的定义为:每一行递增,每一列也递增。

解析:优先级队列往右和下两个方向遍历,记住两个方向遍历的小技巧,还有上下左右四个方向遍历的技巧,int posX = {0,1,0,-1};int posY = {-1,0,1,0};

 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 }
View Code

4.排序矩阵中的从小到大第k个数follow up ---两个排序数组和的第k小

--给定两个排好序的数组 AB,定义集合 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
View Code

解法:

 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 }
View Code

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
View Code

解法:

 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 }
View Code

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
View Code

解法:

 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 }
View Code

 衍生题--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].
View Code

解法:其实就是求集合的个数,然后在新的岛屿生成后,将上下左右的集合合并就好。

 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 }
View Code

 判断图是否是树

题意:

给出 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.
View Code

--图是连通图,并且不存在环。那么这个图是树

解法:并查集。并查集用来判断是否存在环,同时记下连通块的个数,最后只剩一个连通块并且没有环,则就是树。

 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 }
View Code

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 }
View Code

 2.单词搜索II

题意:

给出一个由小写字母组成的矩阵和一个字典。找出所有同时在字典和矩阵中出现的单词。一个单词可以从矩阵中的任意位置开始,可以向左/右/上/下四个相邻方向移动。

您在真实的面试中是否遇到过这个题? Yes
样例
给出矩阵:
doaf
agai
dcan
和字典:
{"dog", "dad", "dgdg", "can", "again"}

返回 {"dog", "dad", "can", "again"}
View Code

解法:利用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 }
View Code

 第三节 高级数据结构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.

Trapping Rain Water

Example

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 }
View Code

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 };
View Code

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  */
View Code

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;
View Code

解法:与上一题类似,利用两个堆,但是这里是滑动窗口,所以每一步需要删除掉出窗口的元素,而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 }
View Code

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 };
View Code
posted @ 2017-06-06 17:47  fisherinbox  阅读(414)  评论(0编辑  收藏  举报