【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
相关文章
相关标签/搜索