【LeetCode】線段樹 segment-tree(共9題)+ 樹狀數組 binary-indexed-tree(共5題)

第一部分---線段樹:https://leetcode.com/tag/segment-tree/  

【218】The Skyline Problem 數組

【307】Range Sum Query - Mutable 網絡

【308】Range Sum Query 2D - Mutable ide

【315】Count of Smaller Numbers After Self 學習

【493】Reverse Pairs ui

【699】Falling Squares (個人線段樹第一題,2019年1月24日)this

在 X 軸上落方塊,問最後整個區間內的最高的高度是多少。 google

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)spa

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

  

【715】Range Module 3d

【732】My Calendar III code

 

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

 

第二部分---樹狀數組: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 };
View Code

 

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

 

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

 

【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].

數據規模:

  1. The length of the given array will not exceed 50,000.
  2. 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 };
View Code
相關文章
相關標籤/搜索