赞
踩
int[] segT = new int[4*n]
(为什么数组大小是4*n???评论区欢迎留言)
tips:长方格中的left、right,分别代表该节点所求得的区间和。例如,left:0,right:4。代表nums中索引0到4的和=2+5+1+4+3=15。
public void build(int index, int left, int right) { // 2.1.1的工作,递归条件,不可再分区间 if (left == right) { segT[index] = nums[left]; return; } // 2.1.1的工作,区间多次二分 int mid = (right - left) / 2 + left; // 左子树根节点的索引 int leftIndex = index * 2; // 右子树根节点的索引 int rightIndex= index * 2 + 1; // 构建左、右子树 build(leftIndex, left, mid); build(rightIndex, mid+1, right); // 2.1.2的工作,求根节点的和 // tips:只有当left==right返回后,才会走这一步 segT[index] = segT[leftIndex] + segT[rightIndex]; }
逻辑其实和建树一样,在线段树中找到修改节点的对应索引,然后修改其值,然后再依次修改根节点的值即可。
public void update(int index, int start, int end, int updateIndex, int value) { // 找到叶子节点,直接修改值(不可再分区间了) if (start == end) { nums[updateIndex] = value; segT[index] = value; return; } int mid = (start + end) / 2; int leftIndex = index * 2; int rightIndex = index * 2 + 1; // 修改的节点再左子树还是右子树 if (updateIndex >= start && updateIndex <= mid) { update(leftIndex, start, mid, updateIndex, value); } else { update(rightIndex, mid + 1, end, updateIndex, value); } segT[index] = segT[leftIndex] + segT[rightIndex]; }
查询的逻辑会稍微复杂一点,因为有三个因素:
public int querySum(int index, int start, int end, int left, int right) { // 查询的索引无效(不是线段树的有意义范围) if (left > end || right < start) { return 0; } // 查询的范围包含该子树的范围,直接返回即可(见下图【包含子树】) else if (left <= start && right >= end) { return st[index].sum; } // 求的左右子树的索引 int mid = (start + end) / 2; int leftIndex = index * 2; int rightIndex = index * 2 + 1; // 左、右子树求得的值 int leftSum = querySum(leftIndex, start, mid, left, right); int rightSum = querySum(rightIndex, mid + 1, end, left, right); return leftSum + rightSum; }
图:包含子树
public class SegmentTree { class Node { int left; int right; int max; int min; int sum; Node() {} Node (int left, int right) { this.left = left; this.right = right; this.max = Integer.MIN_VALUE; this.min = Integer.MAX_VALUE; this.sum = 0; } } int n; Node[] st; int[] nums; public void build(int index, int left, int right) { if (left == right) { st[index].sum = nums[left]; st[index].max = nums[left]; st[index].min = nums[left]; return; } int mid = (right - left) / 2 + left; int leftIndex = index * 2; int rightIndex= index * 2 + 1; build(leftIndex, left, mid); build(rightIndex, mid+1, right); st[index].max = Math.max(st[leftIndex].max, st[rightIndex].max); st[index].min = Math.min(st[leftIndex].min, st[rightIndex].min); st[index].sum = st[leftIndex].sum + st[rightIndex].sum; } public void init(int N, int[] arr) { n = N; st = new Node[4 * n + 1]; for (int i = 1; i <= 4 * n; i++) { st[i] = new Node(); } nums = arr; } public int querySum(int left, int right) { return querySum(1, 0, n-1, left, right); } public int querySum(int index, int start, int end, int left, int right) { if (left > end || right < start) { return 0; } else if (left <= start && right >= end) { return st[index].sum; } int mid = (start + end) / 2; int leftIndex = index * 2; int rightIndex = index * 2 + 1; int leftSum = querySum(leftIndex, start, mid, left, right); int rightSum = querySum(rightIndex, mid + 1, end, left, right); return leftSum + rightSum; } public int queryMax(int left, int right) { return queryMax(1, 0, n-1, left, right); } public int queryMax(int index, int start, int end, int left, int right) { if (left > end || right < start) { return 0; } else if (left <= start && right >= end) { return st[index].sum; } int mid = (start + end) / 2; int leftIndex = index * 2; int rightIndex = index * 2 + 1; int leftMax = queryMax(leftIndex, start, mid, left, right); int rightMax = queryMax(rightIndex, mid + 1, end, left, right); return Math.max(leftMax, rightMax); } public int queryMin(int left, int right) { return queryMin(1, 0, n-1, left, right); } public int queryMin(int index, int start, int end, int left, int right) { if (left > end || right < start) { return Integer.MAX_VALUE; } else if (left <= start && right >= end) { return st[index].sum; } int mid = (start + end) / 2; int leftIndex = index * 2; int rightIndex = index * 2 + 1; int leftMin = queryMin(leftIndex, start, mid, left, right); int rightMin = queryMin(rightIndex, mid + 1, end, left, right); return Math.min(leftMin, rightMin); } public void update(int index, int start, int end, int updateIndex, int value) { if (start == end) { nums[updateIndex] = value; st[index].sum = value; st[index].max = value; st[index].min = value; return; } int mid = (start + end) / 2; int leftIndex = index * 2; int rightIndex = index * 2 + 1; if (updateIndex >= start && updateIndex <= mid) { update(leftIndex, start, mid, updateIndex, value); } else { update(rightIndex, mid + 1, end, updateIndex, value); } st[index].max = Math.max(st[leftIndex].max, st[index].max); st[index].min = Math.min(st[leftIndex].min, st[index].min); st[index].sum = st[leftIndex].sum + st[index].sum; } public void update(int index, int value) { nums[index] = value; update(1, 0, n - 1, index, value); } }
leetcode307 区域和检索 - 数组可修改
并不是所有求区间和都能用线段树,例如leetcode327 区间和的个数,元素的值范围是 -231<=num<= 231-1,如果直接构建线段树,空间复杂度会挤爆!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。