307. 區域和檢索 - 陣列可修改(樹狀陣列,線段樹)
阿新 • • 發佈:2020-09-01
方法一:樹狀陣列
class NumArray { int[] nums; int[] bitArr; int n; public NumArray(int[] nums) { n = nums.length; this.nums = nums; bitArr = new int[n+1]; for(int i = 1; i <= n; i++) bitArr[i] = nums[i-1]; for(int i = 1; i <= n; i++) {int j = i + lowbit(i); if(j <= n) bitArr[j] += bitArr[i]; } } public int lowbit(int x) {return x&(-x);} public void update(int i, int val) { int diff = val - nums[i]; nums[i] = val; i++; for(int j = i; j <= n; j += lowbit(j)) { bitArr[j]+= diff; } } public int prefixSum(int x) { int res = 0; x++; for(int i = x; i > 0; i -= lowbit(i)) { res += bitArr[i]; } return res; } public int sumRange(int i, int j) { return prefixSum(j) - prefixSum(i-1); } }
方法二:線段樹
class NumArray { private Node root; int[] nums; public NumArray(int[] nums) { if(nums != null && nums.length > 0) { this.nums = nums; root = new Node(0,nums.length-1); // 根節點包含全部區間 buildTree(root); // 建樹 } } public int buildTree(Node root) { int l = root.l, r = root.r; if(l == r) { // 遞迴終止條件為到達葉節點 root.sum = nums[l]; return root.sum; } int mid = (l + r) / 2; root.left = new Node(l,mid); root.right = new Node(mid+1,r); int lval = buildTree(root.left); int rval = buildTree(root.right); // 左右遞迴建樹, 返回值為區間和 root.sum = lval + rval; return root.sum; } public void update(int i, int val) { set(root,i,val); } public void set(Node root, int i, int val) { //遞迴更新單值 if(root.l == root.r && root.l == i) { root.sum = val; // 終止條件為找到單值 return; } int l = root.l, r = root.r; int mid = (l + r) / 2; if(i <= mid) { set(root.left,i,val); } else { set(root.right,i,val); } // 每次遞迴結束回到上一個遞迴棧都要同時跟新父節點的區間和 root.sum = root.left.sum + root.right.sum; return; } public int sumRange(int i, int j) { return query(i,j,root); } public int query(int i, int j, Node root) { // 遞迴查詢某一段區間和 if(root.l == i && root.r == j) { return root.sum;// 遞迴終止條件為找到這一段區間,返回區間和 } int mid = (root.l + root.r) / 2; if(i > mid) { // [l,mid]為左子樹 i > mid 向右子樹遞迴 return query(i,j,root.right); } if(j < mid + 1) { // [mid+1,r]為右子樹 j < mid+1 向左子樹遞迴 return query(i,j,root.left); } //這種情況為i與j在mid兩端所以要取[i,mid]和[mid+1,j]的和 return query(i,mid,root.left) + query(mid+1,j,root.right); } } class Node { //定義區間樹的節點 int l, r; // 區間的左值和右值 int sum; // 區間總和 Node left,right; // 左右節點 public Node(int l, int r) { this.l = l; this.r = r; } }