第一部分---線段樹:https://leetcode.com/tag/segment-tree/
【218】The Skyline Problem
【307】Range Sum Query - Mutable
【308】Range Sum Query 2D - Mutable
【315】Count of Smaller Numbers After Self
【493】Reverse Pairs
【699】Falling Squares (我的線段樹第一題,2019年1月24日)
在 X 軸上落方塊,問最后整個區間內的最高的高度是多少。
Input: [[1, 2], [2, 3], [6, 1]] Output: [2, 5, 5] Explanation: After the first drop of positions[0] = [1, 2]: _aa _aa ------- The maximum height of any square is 2. After the second drop of positions[1] = [2, 3]: __aaa __aaa __aaa _aa__ _aa__ -------------- The maximum height of any square is 5. The larger square stays on top of the smaller square despite where its center of gravity is, because squares are infinitely sticky on their bottom edge. After the third drop of positions[1] = [6, 1]: __aaa __aaa __aaa _aa _aa___a -------------- The maximum height of any square is still 5. Thus, we return an answer of [2, 5, 5].
題解:用了線段樹的單點更新,只能beats 1.9% == 如果用區間更新的話, 應該快很多。但是這是第一題線段樹,紀念一下。(我的線段樹寫的都是 base 0)

1 class Solution { 2 public: 3 const static int MAX_SIZE = 1 << 15; 4 struct SegmentTree { 5 void init(int _n) { 6 n = 1; 7 while (n < _n) { 8 n *= 2; 9 } 10 for (int i = 0; i < n * 2 -1; ++i) { dat[i] = 0; } 11 } 12 #define lson(k) k*2+1 13 #define rson(k) k*2+2 14 #define father(k) (k-1)/2 15 inline void pushup(int k) { dat[k] = max(dat[lson(k)], dat[rson(k)]); } 16 void update(int k, int value) { 17 k += n - 1; 18 dat[k] = value; 19 while (k > 0) { 20 k = (k-1)/2; 21 pushup(k); 22 } 23 } 24 int query(int a, int b, int k, int l, int r) { 25 if (r <= a || b <= l) { return 0; } 26 if (a <= l && r <= b) { 27 return dat[k]; 28 } else { 29 int vl = query(a, b, lson(k), l, (l+r)/2); 30 int vr = query(a, b, rson(k), (l+r)/2, r); 31 return max(vl, vr); 32 } 33 } 34 void print() { 35 for (int i = 0; i < 2 * n - 1; ++i) { 36 printf("%d ", dat[i]); 37 } 38 printf("\n"); 39 } 40 int n, dat[MAX_SIZE]; 41 }; 42 vector<int> fallingSquares(vector<pair<int, int>>& positions) { 43 int size = positions.size(); 44 set<int> st; 45 for (auto pos : positions) { 46 st.insert(pos.first), 47 st.insert(pos.first + pos.second - 1); 48 } 49 vector<int> nums(st.begin(), st.end()); 50 SegmentTree seg; 51 seg.init((int)st.size()); 52 vector<int> ans; 53 for (auto pos : positions) { 54 int l = pos.first, r = pos.first + pos.second - 1, h = pos.second; 55 int idxL = distance(st.begin(), st.find(l)), idxR = distance(st.begin(), st.find(r)); 56 int base = seg.query(idxL, idxR+1, 0, 0, seg.n); 57 for (int i = idxL; i <= idxR; ++i) { 58 seg.update(i, base + h); 59 } 60 int maxx = seg.query(0, (int)st.size(), 0, 0, seg.n); 61 ans.push_back(maxx); 62 } 63 return ans; 64 } 65 };
【715】Range Module
【732】My Calendar III
【850】Rectangle Area II (2019年3月15日,google tag)重疊矩形求面積
題解:我們需要一個新的grid,然后去標記grid上的每個格子是不是被矩形覆蓋。grid可以不均勻,(離散化思想)。具體來說,將所有的X坐標集中起來(要去除重復),將所有的Y坐標集中起來,然后將其兩兩配對組成一個二維的網絡。
然后對於每一個矩形,去grid上標記grid上的方格是不是被覆蓋,被覆蓋的話標記為 true,這個小方格需要計算面積。簡單來說:在這個網絡中找到每個矩形所框起來的范圍(遵循左閉右開的原則),標記這個范圍內的網格點為true,意味着這些網格點是落在被cover的面積里。遍歷完所有的矩形后,所有標記為true的網格點都是要被算入面積的,而那些沒有標記的說明不用被計算。
然后把 grid 上標記為 true 的小方格的面積加起來就可以了。

1 class Solution { 2 public: 3 int rectangleArea(vector<vector<int>>& rectangles) { 4 set<int> x_axis, y_axis; 5 for (auto& r : rectangles) { 6 x_axis.insert(r[0]), 7 x_axis.insert(r[2]), 8 y_axis.insert(r[1]), 9 y_axis.insert(r[3]); 10 } 11 vector<int> x(x_axis.begin(), x_axis.end()), y(y_axis.begin(), y_axis.end()); 12 vector<vector<int>> grid(y.size(), vector<int>(x.size(), 0)); 13 for (auto& r : rectangles) { 14 int xleft = distance(x_axis.begin(), x_axis.lower_bound(r[0])); 15 int xright = distance(x_axis.begin(), x_axis.lower_bound(r[2])); 16 int ybuttom = distance(y_axis.begin(), y_axis.lower_bound(r[1])); 17 int ytop = distance(y_axis.begin(), y_axis.lower_bound(r[3])); 18 for (int x0 = xleft; x0 < xright; ++x0) { 19 for (int y0 = ybuttom; y0 < ytop; ++y0) { 20 grid[y0][x0] = 1; 21 } 22 } 23 } 24 long res = 0; 25 const int mod = 1e9+7; 26 for (int y0 = 0; y0 < grid.size(); ++y0) { 27 for (int x0 = 0; x0 < grid[y0].size(); ++x0) { 28 if (grid[y0][x0]) { 29 res += long(x[x0+1] - x[x0]) * long(y[y0+1] - y[y0]); 30 res %= mod; 31 } 32 } 33 } 34 return res; 35 } 36 };
第二部分---樹狀數組:https://leetcode.com/tag/binary-indexed-tree/
【218】The Skyline Problem (2019年1月22日)
本題想不出來用樹狀數組怎么做,最后自己yy出來了一種寫法來做。
給了一堆大樓,給了每個樓的坐標和高度,用 (l, r, h) 表示,返回所有的 key points, A key point is the left endpoint of a horizontal line segment.
For instance, the dimensions of all buildings in Figure A are recorded as: [ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ]
.
For instance, the skyline in Figure B should be represented as:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ]
.
題解:這個題用BIT我是想不出來有什么解法,但是 heap (斜堆) 和 線段樹可以做。我是利用了 C++ STL 里面的 multiset 做的。
我們先用掃描線看這個圖,從左往右掃,如果掃描到了一個building的左邊,說明這個大樓開始了,我們想查看下這個樓的左上角能不能作為 key point,如果能,就把它加到答案里面,如果不能就不加。怎么判斷這個樓的左上角能不能加到答案里面呢?我們先看下它的高度,如果它比前面所有的樓都高,那它的左上角肯定是個 key point, 如果前面有比它高的樓並且這個樓還沒有結束,那么他就不是一個 key point。如果掃描到了一個大樓的右邊,說明這個樓結束了,那么這個樓的右邊界的坐標能不能做 key point 呢?如果它前面有樓比它高,就不能,如果前面有樓跟它一樣高,還是不能,只有把它刪除之后,剩下所有的樓都比它矮,它才能做 key point。所以我們用掃描線依次掃描所有的坐標,就能生成答案。

1 //本題還有個邊界情況是兩個樓高度一樣,如果不交疊,剛好碰上了怎么辦,[[0,2,3],[2,5,3]] 2 class Solution { 3 public: 4 struct kcmp { 5 bool operator() (const pair<int, int>& p1, const pair<int, int>& p2) const { 6 if (p1.first == p2.first) { 7 return p1.second > p2.second; 8 } 9 return p1.first < p2.first; 10 } 11 }; 12 vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) { 13 for (auto b : buildings) { 14 record.insert(make_pair(b[0], b[2])); 15 record.insert(make_pair(b[1], -b[2]));//離開用負數來記錄 16 } 17 vector<pair<int, int>> ret; 18 for (auto line : record) { 19 bool enter = line.second > 0 ? true : false; 20 int h = abs(line.second); 21 if (enter) { //如果這條線上有個樓進來了 22 if (h > getMaxHeight()) { 23 ret.push_back(make_pair(line.first, h)); 24 } 25 stHeight.insert(h); 26 } else { //如果這條線有個樓出去了,可能會往ret里面加個低的點,或者前面還有樓比它高的話,就把這條線給扔了 27 auto iter = stHeight.find(h); 28 stHeight.erase(iter); 29 if (h > getMaxHeight()) { 30 ret.push_back(make_pair(line.first, getMaxHeight())); 31 } 32 } 33 } 34 return ret; 35 } 36 multiset<pair<int, int>, kcmp> record; 37 multiset<int> stHeight; 38 int getMaxHeight() { 39 if (stHeight.empty()) { 40 return 0; 41 } 42 return *stHeight.rbegin(); 43 } 44 };
【307】Range Sum Query - Mutable (2019年1月14日,學習BIT)
實現一個一維的樹狀數組模板。

1 class NumArray { 2 public: 3 NumArray(vector<int> nums) { 4 n = nums.size(); 5 this->nums.resize(n+1); 6 bit.resize(n+1); 7 for (int i = 1; i <= n; ++i) { 8 this->nums[i] = nums[i-1]; 9 add(i, this->nums[i]); 10 } 11 } 12 void update(int i, int val) { 13 add(i+1, val - nums[i+1]); 14 nums[i + 1] = val; 15 } 16 int sumRange(int i, int j) { 17 ++i, ++j; 18 return sum(j) - sum(i-1); 19 } 20 int lowbit(int x) { 21 return x & -x; 22 } 23 void add(int x, int v) { 24 for (int i = x; i <= n; i += lowbit(i)) { 25 bit[i] += v; 26 } 27 } 28 int sum(int x) { 29 int ret = 0; 30 for (int i = x; i > 0; i -= lowbit(i)) { 31 ret += bit[i]; 32 } 33 return ret; 34 } 35 vector<int> nums, bit; 36 int n; 37 }; 38 39 /** 40 * Your NumArray object will be instantiated and called as such: 41 * NumArray obj = new NumArray(nums); 42 * obj.update(i,val); 43 * int param_2 = obj.sumRange(i,j); 44 */
【308】Range Sum Query 2D - Mutable (2019年1月14日,學習BIT)
【315】Count of Smaller Numbers After Self (2019年1月22日,Fenwick Tree 練習)
給了一個數組 nums, 返回一個數組,數組中的元素 ret[i] 代表 nums[i] 的右邊有多少個比它小的數。(題目如果換一下, 求每個元素左/右邊有多少個比它小/大/大於等於/小於等於的數)
You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i]
is the number of smaller elements to the right of nums[i]
.
Example: Input: [5,2,6,1] Output: [2,1,1,0] Explanation: To the right of 5 there are 2 smaller elements (2 and 1). To the right of 2 there is only 1 smaller element (1). To the right of 6 there is 1 smaller element (1). To the right of 1 there is 0 smaller element.
題解:這個題第一個想法是dp做,但是嘗試了兩下,完全推導不了。dp做不出來。看了下tag是樹狀數組,還有BST等等等很多種解法。我這次先練習下怎么用樹狀數組解題。
樹狀數組有兩個用途(1)用 O(logN)的時間求前綴和。(2)用log(N)的時間在位置為 i 的元素上增加一個數。
這個題求數組中一個元素右邊有多少個數比它小。我們如果翻轉下數組,就可以變成求數組中一個元素左邊有多少元素比它小。
那么它和 Fenwick Tree 有什么關系呢?
你想啊,我們可以把原數組先排序並且去重,得到一個遞增的並且unique元素的新數組,但是呢這個新數組不能代表原來的數組,因為原來數組中可能有重復元素。所以我們搞出來一個 freq 數組(其實就是我們樹狀數組的原來數組),配合sorted數組使用。
sorted數組里面的元素不是連續的,我們需要把它離散化,求出他們的相對位置。關系如下圖:
nums: [7, 1, 3, 2, 9, 2, 1]
sorted: [1, 2, 3, 7, 9] (1-based)(其實就是我們元素 對應 樹狀數組的下標) sorted[nums[i]] = j
reversed: [1, 2, 9, 2, 3, 1, 7]
rank: [1, 2, 5, 2, 3, 1, 4]
依次遍歷 reversed 數組,先求這個元素前面有多少個元素小於它(樹狀數組的前綴和),增加每個元素的 freq。往下求就ok了。

1 class Solution { 2 public: 3 class FenwickTree { 4 public: 5 FenwickTree(int n): sums_(n+1, 0) {} 6 void add (int i, int delta) { 7 while (i < sums_.size()) { 8 sums_[i] += delta; 9 i += lowbit(i); 10 } 11 } 12 int query (int i) { 13 int sum = 0; 14 while (i > 0) { 15 sum += sums_[i]; 16 i -= lowbit(i); 17 } 18 return sum; 19 } 20 private: 21 static inline int lowbit(int x) {return x & -x;} 22 vector<int> sums_; 23 }; 24 vector<int> countSmaller(vector<int>& nums) { 25 set<int> sorted(nums.begin(), nums.end()); 26 unordered_map<int, int> rank; 27 int r = 0; 28 for (auto num : sorted) { 29 rank[num] = ++r; 30 } 31 vector<int> ret; 32 FenwickTree bit(rank.size()); 33 for (int i = nums.size() - 1; i >= 0; --i) { 34 r = rank[nums[i]]; 35 ret.push_back(bit.query(r-1)); 36 bit.add(r, 1); 37 } 38 reverse(ret.begin(), ret.end()); 39 return ret; 40 } 41 };
【493】Reverse Pairs (2019年1月23日,復習Fenwick Tree 和 學習Segment Tree)
給了一個數組,求數組中逆序對的個數。注意這題的逆序對和我們平常定義的有個非常微小的差異。
Given an array nums
, we call (i, j)
an important reverse pair if i < j
and nums[i] > 2*nums[j]
.
數據規模:
- The length of the given array will not exceed
50,000
. - All the numbers in the input array are in the range of 32-bit integer.
Example1:
Input: [1,3,2,3,1]
Output: 2
Example2:
Input: [2,4,3,5,1]
Output: 3
題解:我是用樹狀數組求的。我們先把數組離散化獲得他們的相對大小。然后用排序好了的去重之后的數組下標來作為bit的原始數組,原始數組中所有元素都為0。我們從頭開始遍歷 nums 數組,對於nums[i]這個元素,首先獲取它離散化之后的下標。然后查詢從 nums[i] * 2 + 1 到排序數組的最大值的這段區間里面的區間和。累加到ret上面就可以了。注意數據規模 nums[i] 最大可以到 INT_MAX, 最小可以到 INT_MIN,所以 nums[i] * 2 + 1 完全可能超過 int 的范圍。

1 class Solution { 2 public: 3 int reversePairs(vector<int>& nums) { 4 const int n = nums.size(); 5 set<int> st(nums.begin(), nums.end()); 6 m = st.size(); 7 summ = vector<int>(m+1, 0); 8 map<int, int> mp; 9 int idx = 1; 10 for (auto e : st) { 11 mp[e] = idx++; 12 } 13 vector<long long> sorted(m+1, 0); //0-based 14 int t = 1; 15 for (auto e : st) { 16 sorted[t++] = e; 17 } 18 int ret = 0; 19 for (int i = 0; i < nums.size(); ++i) { 20 int e = nums[i]; 21 int x = mp[e]; 22 long long target = 2 * (long long)nums[i]; 23 int x1 = distance(sorted.begin(), upper_bound(sorted.begin(), sorted.end(), target)); 24 ret += query(m) - query(x1 - 1); 25 add(x, 1); 26 } 27 return ret; 28 } 29 int m; 30 vector<int> summ; //bit sum 31 void add(int x, int val) { 32 for (int i = x; i <= m; i += lowbit(i)) { 33 summ[i] += val; 34 } 35 } 36 int query(int x) { 37 int res = 0; 38 for (int i = x; i > 0; i -= lowbit(i)) { 39 res += summ[i]; 40 } 41 return res; 42 } 43 int lowbit(int x) { 44 return x & (-x); 45 } 46 void print(map<int, int>& mp) { 47 for (auto p : mp) { 48 cout << p.first << " " << p.second << endl; 49 } 50 } 51 void print(vector<int>& nums) { 52 for (auto e : nums) { 53 cout << e << " " ; 54 } 55 cout << endl; 56 } 57 };