介绍
线段树的题目,起步基本就是hard。
其实线段树就是一种经典空间换时间,用一维度的空间降了一维度的时间。当然,使用线段树也要满足一些条件,即数据的组织结构要有特点。
一个不错的讲解可以看:https://www.acwing.com/blog/content/3369/
线段树之所以称为“树”,是因为其具有树的结构特性。线段树由于本身是专门用来处理区间问题的(包括 RMQRMQ 、 RSQRSQ) 问题等。
对于每一个子节点而言,都表示整个序列中的一段子区间;对于每个叶子节点而言,都表示序列中的单个元素信息;子节点不断向自己的父亲节点传递信息,而父节点存储的信息则是他的每一个子节点信息的整合。
线段树就是分块思想的树化,或者说是对于信息处理的二进制化——用于达到 O(logn)级别的处理速度, log以 2为底。而分块的思想,则是可以用一句话总结为:通过将整个序列分为有穷个小块,对于要查询的一段区间,总是可以整合成 k 个所分块与 m 个单个元素的信息的并 (0<=k,m<=sqrt(n))(0<=k,m<=n) 。但普通的分块不能高效率地解决很多问题,所以作为 log级别的数据结构,线段树应运而生。
如果你只是想像力扣303和307题解给个ac的答案,那么这两题其实也没必要去做了,其实这两题本质应该是练习线段树。
leetcode303区域和检索 - 数组不可变,如下:
给定一个整数数组 nums
,处理以下类型的多个查询:
计算索引
left
和right
(包含left
和right
)之间的nums
元素的 和 ,其中left <= right
实现 NumArray
类:
NumArray(int[] nums)
使用数组nums
初始化对象int sumRange(int i, int j)
返回数组nums
中索引left
和right
之间的元素的 总和 ,包含left
和right
两点(也就是nums[left] + nums[left + 1] + ... + nums[right]
)
示例 1:
输入:
["NumArray", "sumRange", "sumRange", "sumRange"]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
输出:
[null, 1, -1, -3]
解释:
NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3)
numArray.sumRange(2, 5); // return -1 (3 + (-5) + 2 + (-1))
numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1))
其实本题就是线段树的模板。主要是线段树的建立和查询。
class NumArray {
public:
struct node
{
int sum=0;
long long left=-999;
long long right=-999;
}newnums[1000000];
vector<int> seq;
int ans=0;
void build(int i,int left,int right)
{
newnums[i].left=left;
newnums[i].right=right;
if(left==right)
{
newnums[i].sum=seq[left];
return;
}
int mid=(left+right)/2;
build(i*2,left,mid);
build(i*2+1,mid+1,right);
newnums[i].sum=newnums[i*2+1].sum+newnums[i*2].sum;
}
void findsum(int k,int i,int j)
{
if(newnums[k].left>=i&&newnums[k].right<=j)
{
// cout<<newnums[k].sum<<endl;
ans+=newnums[k].sum;
return;
}
int mid=(newnums[k].left+newnums[k].right)>>1;
if(i<=mid) findsum(k*2,i,j);
if(j>mid) findsum(k*2+1,i,j);
}
NumArray(vector<int>& nums) {
seq.push_back(0);
for(int i=0;i<nums.size();i++)
{
seq.push_back(nums[i]);
}
if(nums.size()!=0)build(1,1,nums.size());
}
int sumRange(int i, int j) {
findsum(1,i+1,j+1);
int final=ans;
ans=0;
return final;
}
};
/**
* Your NumArray object will be instantiated and called as such:
* NumArray* obj = new NumArray(nums);
* int param_1 = obj->sumRange(i,j);
*/
leetcode307. 区域和检索 - 数组可修改,如下:
给你一个数组 nums
,请你完成两类查询。
其中一类查询要求 更新 数组
nums
下标对应的值另一类查询要求返回数组
nums
中索引left
和索引right
之间( 包含 )的nums元素的 和 ,其中left <= right
实现 NumArray
类:
NumArray(int[] nums)
用整数数组nums
初始化对象void update(int index, int val)
将nums[index]
的值 更新 为val
int sumRange(int left, int right)
返回数组nums
中索引left
和索引right
之间( 包含 )的nums元素的 和 (即,nums[left] + nums[left + 1], ..., nums[right]
)
示例 1:
输入:
["NumArray", "sumRange", "update", "sumRange"]
[[[1, 3, 5]], [0, 2], [1, 2], [0, 2]]
输出:
[null, 9, null, 8]
解释:
NumArray numArray = new NumArray([1, 3, 5]);
numArray.sumRange(0, 2); // 返回 1 + 3 + 5 = 9
numArray.update(1, 2); // nums = [1,2,5]
numArray.sumRange(0, 2); // 返回 1 + 2 + 5 = 8
线段树的建立 线段树的修改模板题。
class NumArray {
public:
struct node
{
int sum=0;
long long left=-999;
long long right=-999;
}newnums[100000];
vector<int> seq;
int ans=0;
void build(int i,int left,int right)
{
newnums[i].left=left;
newnums[i].right=right;
if(left==right)
{
newnums[i].sum=seq[left];
return;
}
int mid=(left+right)/2;
build(i*2,left,mid);
build(i*2+1,mid+1,right);
newnums[i].sum=newnums[i*2+1].sum+newnums[i*2].sum;
}
void findsum(int k,int i,int j)
{
if(newnums[k].left>=i&&newnums[k].right<=j)
{
// cout<<newnums[k].sum<<endl;
ans+=newnums[k].sum;
return;
}
int mid=(newnums[k].left+newnums[k].right)>>1;
if(i<=mid) findsum(k*2,i,j);
if(j>mid) findsum(k*2+1,i,j);
}
void change(int origin,int i,int val)
{
if(newnums[i].left==newnums[i].right)
{
newnums[i].sum=val;
return;
}
int mid=(newnums[i].left+newnums[i].right)/2;
if(origin<=mid) change(origin,i*2,val);
else change(origin,i*2+1,val);
newnums[i].sum=newnums[i*2].sum+newnums[i*2+1].sum;
}
NumArray(vector<int>& nums) {
seq.push_back(0);
for(int i=0;i<nums.size();i++)
{
seq.push_back(nums[i]);
}
if(nums.size()!=0)build(1,1,nums.size());
}
void update(int i,int val)
{
change(i+1,1,val);
}
int sumRange(int i, int j) {
findsum(1,i+1,j+1);
int final=ans;
ans=0;
return final;
}
};
/**
* Your NumArray object will be instantiated and called as such:
* NumArray* obj = new NumArray(nums);
* int param_1 = obj->sumRange(i,j);
*/