参考

实现一

可用于以下两种情况

  • 单点更新、区间查询
  • 区间更新、单点查询

    1. public class TreeArray {
    2. // 树状数组,下标从1开始
    3. private int[] value;
    4. // 长度
    5. private final int length;
    6. public TreeArray(int n) {
    7. length = n;
    8. value = new int[length + 1];
    9. }
    10. public TreeArray(int[] arr) {
    11. length = arr.length;
    12. value = new int[length + 1];
    13. for (int i = 1; i <= length; ++i) {
    14. update(i, arr[i - 1]);
    15. }
    16. }
    17. /**
    18. * k:x的二进制形式从最低位到最高位连续0的个数
    19. * 求2^k
    20. *
    21. * @param x 非负整数
    22. * @return 2^k
    23. */
    24. private int lowBit(int x) {
    25. return x & (-x);
    26. }
    27. /**
    28. * 原数组的第i个元素增加k,更新树状数组的值
    29. *
    30. * @param i 原数组元素索引位置
    31. * @param k 更新值
    32. */
    33. public void update(int i, int k) {
    34. while (i <= length) {
    35. value[i] += k;
    36. i += lowBit(i);
    37. }
    38. }
    39. /**
    40. * 求[1, i]这i个数的和
    41. *
    42. * @param i 索引位置
    43. * @return 前i个数的和
    44. */
    45. public int getSum(int i) {
    46. int sum = 0;
    47. while (i >= 1) {
    48. sum += value[i];
    49. i -= lowBit(i);
    50. }
    51. return sum;
    52. }
    53. }

单点更新、区间查询如何建树和使用

  1. int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  2. TreeArray t = new TreeArray(a);
  3. System.out.println(t.getSum(10) - t.getSum(4));

或者

  1. int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  2. TreeArray t = new TreeArray(a.length);
  3. for (int i = 1; i <= a.length; ++i) {
  4. t.update(i, a[i - 1]);
  5. }
  6. System.out.println(t.getSum(10) - t.getSum(4));

区间更新、单点查询如何建树和使用

  1. int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  2. TreeArray t = new TreeArray(a.length);
  3. t.update(1, a[0]);
  4. for (int i = 2; i <= a.length; ++i) {
  5. t.update(i, a[i - 1] - a[i - 2]);
  6. }
  7. for (int i = 1; i <= 10; ++i) {
  8. System.out.print(t.getSum(i) + " ");
  9. }
  10. System.out.println();
  11. // [x,y]区间内的数均加上k
  12. int x = 5;
  13. int y = 7;
  14. int k = 10;
  15. t.update(x, k);
  16. t.update(y + 1, -k);
  17. for (int i = 1; i <= 10; ++i) {
  18. System.out.print(t.getSum(i) + " ");
  19. }

实现二

可用于以下情况

  • 区间更新、区间查询

    1. public class TreeArray {
    2. // 树状数组,下标从1开始,差分值D[i],用于求单点值
    3. private int[] sum1;
    4. // 树状数组,下标从1开始,D[i]*(i-1),用于求前缀和
    5. private int[] sum2;
    6. // 长度
    7. private final int length;
    8. public TreeArray(int[] arr) {
    9. length = arr.length;
    10. sum1 = new int[length + 1];
    11. sum2 = new int[length + 1];
    12. update(1, arr[0]);
    13. for (int i = 2; i <= length; ++i) {
    14. update(i, arr[i - 1] - arr[i - 2]);
    15. }
    16. }
    17. /**
    18. * k:x的二进制形式从最低位到最高位连续0的个数
    19. * 求2^k
    20. *
    21. * @param x 非负整数
    22. * @return 2^k
    23. */
    24. private int lowBit(int x) {
    25. return x & (-x);
    26. }
    27. /**
    28. * 原数组的第i个元素增加k,更新树状数组的值
    29. *
    30. * @param i 原数组元素索引位置
    31. * @param k 更新值
    32. */
    33. public void update(int i, int k) {
    34. int i_ = i;
    35. while (i <= length) {
    36. sum1[i] += k;
    37. sum2[i] += k * (i_ - 1);
    38. i += lowBit(i);
    39. }
    40. }
    41. /**
    42. * 求[1, i]这i个数的和
    43. *
    44. * @param i 索引位置
    45. * @return 前i个数的和
    46. */
    47. public int getSum(int i) {
    48. int sum = 0;
    49. int i_ = i;
    50. while (i >= 1) {
    51. sum += i_ * sum1[i] - sum2[i];
    52. i -= lowBit(i);
    53. }
    54. return sum;
    55. }
    56. /**
    57. * 区间更新,[l,r]区间内的数均加上k
    58. *
    59. * @param l 左边界
    60. * @param r 右边界
    61. * @param k 更新值
    62. */
    63. public void sectionUpdate(int l, int r, int k) {
    64. update(l, k);
    65. update(r + 1, -k);
    66. }
    67. }

区间更新、区间查询如何建树和使用

  1. int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  2. TreeArray t = new TreeArray(a);
  3. System.out.println(t.getSum(10) - t.getSum(4));
  4. // [x,y]区间内的数均加上k
  5. int x = 5;
  6. int y = 7;
  7. int k = 10;
  8. t.sectionUpdate(x, y, k);
  9. System.out.println(t.getSum(10) - t.getSum(4));