【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
【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)
class Solution {
public:
const static int MAX_SIZE = << ;
struct SegmentTree {
void init(int _n) {
n = ;
while (n < _n) {
n *= ;
}
for (int i = ; i < n * -; ++i) { dat[i] = ; }
}
#define lson(k) k*2+1
#define rson(k) k*2+2
#define father(k) (k-1)/2
inline void pushup(int k) { dat[k] = max(dat[lson(k)], dat[rson(k)]); }
void update(int k, int value) {
k += n - ;
dat[k] = value;
while (k > ) {
k = (k-)/;
pushup(k);
}
}
int query(int a, int b, int k, int l, int r) {
if (r <= a || b <= l) { return ; }
if (a <= l && r <= b) {
return dat[k];
} else {
int vl = query(a, b, lson(k), l, (l+r)/);
int vr = query(a, b, rson(k), (l+r)/, r);
return max(vl, vr);
}
}
void print() {
for (int i = ; i < * n - ; ++i) {
printf("%d ", dat[i]);
}
printf("\n");
}
int n, dat[MAX_SIZE];
};
vector<int> fallingSquares(vector<pair<int, int>>& positions) {
int size = positions.size();
set<int> st;
for (auto pos : positions) {
st.insert(pos.first),
st.insert(pos.first + pos.second - );
}
vector<int> nums(st.begin(), st.end());
SegmentTree seg;
seg.init((int)st.size());
vector<int> ans;
for (auto pos : positions) {
int l = pos.first, r = pos.first + pos.second - , h = pos.second;
int idxL = distance(st.begin(), st.find(l)), idxR = distance(st.begin(), st.find(r));
int base = seg.query(idxL, idxR+, , , seg.n);
for (int i = idxL; i <= idxR; ++i) {
seg.update(i, base + h);
}
int maxx = seg.query(, (int)st.size(), , , seg.n);
ans.push_back(maxx);
}
return ans;
}
};
【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 的小方格的面积加起来就可以了。
class Solution {
public:
int rectangleArea(vector<vector<int>>& rectangles) {
set<int> x_axis, y_axis;
for (auto& r : rectangles) {
x_axis.insert(r[]),
x_axis.insert(r[]),
y_axis.insert(r[]),
y_axis.insert(r[]);
}
vector<int> x(x_axis.begin(), x_axis.end()), y(y_axis.begin(), y_axis.end());
vector<vector<int>> grid(y.size(), vector<int>(x.size(), ));
for (auto& r : rectangles) {
int xleft = distance(x_axis.begin(), x_axis.lower_bound(r[]));
int xright = distance(x_axis.begin(), x_axis.lower_bound(r[]));
int ybuttom = distance(y_axis.begin(), y_axis.lower_bound(r[]));
int ytop = distance(y_axis.begin(), y_axis.lower_bound(r[]));
for (int x0 = xleft; x0 < xright; ++x0) {
for (int y0 = ybuttom; y0 < ytop; ++y0) {
grid[y0][x0] = ;
}
}
}
long res = ;
const int mod = 1e9+;
for (int y0 = ; y0 < grid.size(); ++y0) {
for (int x0 = ; x0 < grid[y0].size(); ++x0) {
if (grid[y0][x0]) {
res += long(x[x0+] - x[x0]) * long(y[y0+] - y[y0]);
res %= mod;
}
}
}
return res;
}
};
第二部分---树状数组: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。所以我们用扫描线依次扫描所有的坐标,就能生成答案。
//本题还有个边界情况是两个楼高度一样,如果不交叠,刚好碰上了怎么办,[[0,2,3],[2,5,3]]
class Solution {
public:
struct kcmp {
bool operator() (const pair<int, int>& p1, const pair<int, int>& p2) const {
if (p1.first == p2.first) {
return p1.second > p2.second;
}
return p1.first < p2.first;
}
};
vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {
for (auto b : buildings) {
record.insert(make_pair(b[], b[]));
record.insert(make_pair(b[], -b[]));//离开用负数来记录
}
vector<pair<int, int>> ret;
for (auto line : record) {
bool enter = line.second > ? true : false;
int h = abs(line.second);
if (enter) { //如果这条线上有个楼进来了
if (h > getMaxHeight()) {
ret.push_back(make_pair(line.first, h));
}
stHeight.insert(h);
} else { //如果这条线有个楼出去了,可能会往ret里面加个低的点,或者前面还有楼比它高的话,就把这条线给扔了
auto iter = stHeight.find(h);
stHeight.erase(iter);
if (h > getMaxHeight()) {
ret.push_back(make_pair(line.first, getMaxHeight()));
}
}
}
return ret;
}
multiset<pair<int, int>, kcmp> record;
multiset<int> stHeight;
int getMaxHeight() {
if (stHeight.empty()) {
return ;
}
return *stHeight.rbegin();
}
};
【307】Range Sum Query - Mutable (2019年1月14日,学习BIT)
实现一个一维的树状数组模板。
class NumArray {
public:
NumArray(vector<int> nums) {
n = nums.size();
this->nums.resize(n+);
bit.resize(n+);
for (int i = ; i <= n; ++i) {
this->nums[i] = nums[i-];
add(i, this->nums[i]);
}
}
void update(int i, int val) {
add(i+, val - nums[i+]);
nums[i + ] = val;
}
int sumRange(int i, int j) {
++i, ++j;
return sum(j) - sum(i-);
}
int lowbit(int x) {
return x & -x;
}
void add(int x, int v) {
for (int i = x; i <= n; i += lowbit(i)) {
bit[i] += v;
}
}
int sum(int x) {
int ret = ;
for (int i = x; i > ; i -= lowbit(i)) {
ret += bit[i];
}
return ret;
}
vector<int> nums, bit;
int n;
}; /**
* Your NumArray object will be instantiated and called as such:
* NumArray obj = new NumArray(nums);
* obj.update(i,val);
* int param_2 = obj.sumRange(i,j);
*/
【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: [,,,]
Output: [,,,]
Explanation:
To the right of there are smaller elements ( and ).
To the right of there is only smaller element ().
To the right of there is smaller element ().
To the right of there is 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了。
class Solution {
public:
class FenwickTree {
public:
FenwickTree(int n): sums_(n+, ) {}
void add (int i, int delta) {
while (i < sums_.size()) {
sums_[i] += delta;
i += lowbit(i);
}
}
int query (int i) {
int sum = ;
while (i > ) {
sum += sums_[i];
i -= lowbit(i);
}
return sum;
}
private:
static inline int lowbit(int x) {return x & -x;}
vector<int> sums_;
};
vector<int> countSmaller(vector<int>& nums) {
set<int> sorted(nums.begin(), nums.end());
unordered_map<int, int> rank;
int r = ;
for (auto num : sorted) {
rank[num] = ++r;
}
vector<int> ret;
FenwickTree bit(rank.size());
for (int i = nums.size() - ; i >= ; --i) {
r = rank[nums[i]];
ret.push_back(bit.query(r-));
bit.add(r, );
}
reverse(ret.begin(), ret.end());
return ret;
}
};
【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 的范围。
class Solution {
public:
int reversePairs(vector<int>& nums) {
const int n = nums.size();
set<int> st(nums.begin(), nums.end());
m = st.size();
summ = vector<int>(m+, );
map<int, int> mp;
int idx = ;
for (auto e : st) {
mp[e] = idx++;
}
vector<long long> sorted(m+, ); //0-based
int t = ;
for (auto e : st) {
sorted[t++] = e;
}
int ret = ;
for (int i = ; i < nums.size(); ++i) {
int e = nums[i];
int x = mp[e];
long long target = * (long long)nums[i];
int x1 = distance(sorted.begin(), upper_bound(sorted.begin(), sorted.end(), target));
ret += query(m) - query(x1 - );
add(x, );
}
return ret;
}
int m;
vector<int> summ; //bit sum
void add(int x, int val) {
for (int i = x; i <= m; i += lowbit(i)) {
summ[i] += val;
}
}
int query(int x) {
int res = ;
for (int i = x; i > ; i -= lowbit(i)) {
res += summ[i];
}
return res;
}
int lowbit(int x) {
return x & (-x);
}
void print(map<int, int>& mp) {
for (auto p : mp) {
cout << p.first << " " << p.second << endl;
}
}
void print(vector<int>& nums) {
for (auto e : nums) {
cout << e << " " ;
}
cout << endl;
}
};
【LeetCode】线段树 segment-tree(共9题)+ 树状数组 binary-indexed-tree(共5题)的更多相关文章
- 树状数组(Binary Indexed Tree) 总结
1.“树状数组”数据结构的一种应用 对含有n个元素的数组(a[1],...,a[k],...,a[n]): (1)求出第i个到第j个元素的和,sum=a[i]+...+a[j]. 进行j-i+1次加法 ...
- 树状数组 Binary Indexed Tree/Fenwick Tree
2018-03-25 17:29:29 树状数组是一个比较小众的数据结构,主要应用领域是快速的对mutable array进行区间求和. 对于一般的一维情况下的区间和问题,一般有以下两种解法: 1)D ...
- 树状数组(Binary Indexed Tree(BIT))
先不说别的,这个博客为我学习树状数组提供了很大帮助,奉上传送门 http://blog.csdn.net/int64ago/article/details/7429868 然后就说几个常用的操作 in ...
- 树状数组(Binary Index Tree)
一维BIT(单点更新,区间求和): Problem - 1166 #include <iostream> #include <algorithm> #include <c ...
- LeetCode第[98]题(Java):Validate Binary Search Tree(验证二叉搜索树)
题目:验证二叉搜索树 难度:Medium 题目内容: Given a binary tree, determine if it is a valid binary search tree (BST). ...
- 【leetcode刷题笔记】Validate Binary Search Tree
Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is defined as ...
- Leetcode: Range Sum Query 2D - Mutable && Summary: Binary Indexed Tree
Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper lef ...
- 树状数组(Binary Indexed Tree,BIT)
树状数组(Binary Indexed Tree) 前面几篇文章我们分享的都是关于区间求和问题的几种解决方案,同时也介绍了线段树这样的数据结构,我们从中可以体会到合理解决方案带来的便利,对于大部分区间 ...
- 树状数组(Binary Indexed Tree)
树状数组(Binary Indexed Tree,BIT) 是能够完成下述操作的数据结构. 给一个初始值全为 0 的数列 a1, a2, ..., an (1)给定 i,计算 a1+a2+...+ai ...
- 树状数组,Fenwick Tree
Fenwick Tree, (also known as Binary Indexed Tree,二叉索引树), is a high-performance data structure to cal ...
随机推荐
- window10 安装 docker
0.打开Hyper-V. 安装成功后,查看“任务管理器”: 1.下载安装程序. 2.一路"next"安装.安装成功后,桌面会出现图标: 3.双击该图标,成功运行后,右小角出现: 右 ...
- .Net Core 学习目录(搁置)
简介 .NET Core 是.NET Framework的新一代版本,是微软开发的第一个官方版本,具有跨平台 (Windows.Mac OSX.Linux) 能力的应用程序开发框架 (Applicat ...
- iOS设计模式之桥接模式
一,什么是桥接模式 定义 抽象出层次结构.上层抽象接口的职能,实现上层抽象接口的职能,层级间的通信协议(可以抽象为接口).桥接模式的目的,就是把抽象层次结构从具体的实现中分离出来,使其能够独立变更.抽 ...
- Oracle中动态SQL详解(EXECUTE IMMEDIATE)
Oracle中动态SQL详解(EXECUTE IMMEDIATE) 2017年05月02日 18:35:48 悠悠倾我心 阅读数:744 标签: oracle动态sqloracle 更多 个人分类: ...
- Python分布式爬虫打造搜索引擎完整版-基于Scrapy、Redis、elasticsearch和django打造一个完整的搜索引擎网站
Python分布式爬虫打造搜索引擎 基于Scrapy.Redis.elasticsearch和django打造一个完整的搜索引擎网站 https://github.com/mtianyan/Artic ...
- 51nod 1205 流水线调度
1205 流水线调度 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 收藏 关注 N个作业{1,2,…,n}要在由2台机器M1和M2组成的流水线上完成加工.每个 ...
- change可以重命名列名,也可能修改列的类型和约束,而modify只能修改列的数据类型。
change 可以重命名列名,也可能修改列的数据类型,而modify只能修改列的数据类型. change 比modify功能多,如果要用change修改字段类型和约束,要加上新字段名,新字段名可以和原 ...
- MySQL工作中遇到的问题记录
1:log_slave_updates: 从库1搭建级联从库2,从库1需要开启log_slave_updates,修改/etc/my.cnf,增加一行log_slave_updates=1,重启数据库 ...
- .Net-WCF-图书:《WCF编程》
ylbtech-.Net-WCF-图书:<WCF编程> <WCF编程>是2008年1月机械工业出版社出版的图书,作者是Juval Lowy.Clemens Vasters. 1 ...
- ubuntu搭建jdk+jenkins
第一步,安装jdk(如果已安装,直接进行第二步) 1.下载 jdk-8u172-linux-x64.tar.gz 点此下载 2.解压 tar -zxvf jdk-8u172-linux ...