线段树是一种二叉搜索树,什么叫做二叉搜索树,首先满足二叉树,每个结点度小于等于二,即每个结点最多有两颗子树,何为搜索,我们要知道,线段树的每个结点都存储了一个区间,也可以理解成一个线段,而搜索,就是在这些线段上进行搜索操作得到你想要的答案。在求一个数组任意的区间和的时候,假如这个数组的数据量很大,单纯的for循环已经不能解决问题了,时间复杂度特别高,通常可以使用前缀和数组的方式进行解决问题,使用前缀和求区间和的时间复杂度为O(1),但是当数组进行更新的时候时间复杂度就会为O(n)。而线段树就要可以每次更新以及查询的时间复杂度为O(logN)。
什么是前缀和:例如一个数组:a[1],a[2],a[3]…a[n],前缀和S[i]表示的是该数组的前i项的和,例如S[3] = a[1] + a[2] + a[3],S[i] = a[1] + a[2] + a[3] + … + a[i - 1] + a[i].
引用leetcode进行简单介绍
给定一个整数数组 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] )
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/range-sum-query-immutable
代码
class NumArray {
int[] sums;
public NumArray(int[] nums) {
int n = nums.length;
sums = new int[n];
sums[0]=nums[0];
for (int i = 1; i < n; i++) {
sums[i] = sums[i-1] + nums[i];
}
}
public int sumRange(int i, int j) {
return i>0?sums[j] - sums[i-1]:sums[j];
}
}
复杂度分析
时间复杂度:初始化 O(n),每次检索 O(1),其中 n 是数组 nums 的长度。
初始化需要遍历数组 nums 计算前缀和,时间复杂度是 O(n)O。
每次检索只需要得到两个下标处的前缀和,然后计算差值,时间复杂度是 O(1)。空间复杂度:O(n),其中 n 是数组ums 的长度。
线段树实例数组
int arr[]=new int[]{1,3,5,7,9,11};
具体代码
public static void main(String[] args) {
int arr[]=new int[]{1,3,5,7,9,11};
int[] tree=new int[1000];
tree[0]=0;
build_tree(arr,tree,0,0,arr.length-1);
// System.out.println(Arrays.toString(tree));
// update_tree(arr,tree,0,0,arr.length-1,4,6);
// System.out.println(Arrays.toString(tree));
int i = query_tree(arr, tree, 0, 0, arr.length - 1, 2, 5);
System.out.println(i);
}
//建造线段树
public static void build_tree(int arr[],int tree[],int node,int start,int end){
if(start==end){
tree[node]=arr[start];
return;
}
int left=2*node+1;
int right=2*node+2;
int mid=(start+end)/2;
build_tree(arr,tree,left,start,mid);
build_tree(arr,tree,right,mid+1,end);
tree[node]=tree[left]+tree[right];
}
//更新某个数值
public static void update_tree(int arr[],int tree[],int node,int start,int end,int index,int val){
if(start==end){
arr[index]=val;
tree[node]=val;
return;
}
int mid=(start+end)/2;
int left=2*node+1;
int right=2*node+2;
if(index<=mid&&index>=start)
update_tree(arr,tree,left,start,mid,index,val);
else
update_tree(arr,tree,right,mid+1,end,index,val);
tree[node]=tree[left]+tree[right];
}
//查询某段数组区间的和
public static int query_tree(int arr[],int tree[],int node,int start, int end,int l,int r){
if(r<start||l>end)
return 0;
else if(start>=l&&end<=r){
return tree[node];
}
else if(start==end){
return tree[node];
}
int mid=(start+end)/2;
int left=2*node+1;
int right=2*node+2;
int left_sum=query_tree(arr,tree,left,start,mid,l,r);
int right_sum=query_tree(arr,tree,right,mid+1,end,l,r);
return left_sum+right_sum;
}