线段树是一种二叉搜索树,什么叫做二叉搜索树,首先满足二叉树,每个结点度小于等于二,即每个结点最多有两颗子树,何为搜索,我们要知道,线段树的每个结点都存储了一个区间,也可以理解成一个线段,而搜索,就是在这些线段上进行搜索操作得到你想要的答案。在求一个数组任意的区间和的时候,假如这个数组的数据量很大,单纯的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; }