1.线段树:

  • 线段树中,叶子节点为数组的元素,线段树中的其他非叶子节点为区间内的最大或者最小/或者总和(根据具体业务而定)

(1)区间染色问题:有一面墙,长度为n,每次选择一段墙染色,m次操作后,问区间[i,k]有多少次颜色。
(2)区间查询

2.线段树的二叉:

image.png

  • 平衡二叉树下,二叉搜索不会退化为链表,平衡二叉树的最大深度与最小深度差距不超过1

    3.线段树的实现:

  • 采用数组实现:假如有 n 个元素, 则上一层节点有 n / 2 个节点,故需要 4n 空间

    1. // 使用数组实现线段树
    2. public class Set<E extends Comparable<E>>{
    3. private E[] data;
    4. private E[] tree;
    5. private Merger<E> merger;
    6. public Set(E[] arr, Merger<E> merger){
    7. data = (E[]) Object[arr.length];
    8. for(int i = 0 ; i < arr.length; i++){
    9. data[i] = arr[i];
    10. }
    11. tree = (E[]) Object[4 * arr.length];
    12. // 建立线段树
    13. buildSegTree(0, 0, data.length - 1);
    14. }
    15. public int leftChild(int index){
    16. return 2 * index + 1;
    17. }
    18. public int rightChild(int index){
    19. return 2 * index + 2;
    20. }
    21. public int parent(int index){
    22. return index - 1 / 2;
    23. }
    24. public void buildSegTree(int treeIndex, int l, int r){
    25. if(l == r){
    26. tree[treeIndex] = data[l];
    27. return;
    28. }
    29. // 每次下一个节点都是 [l, mid][mid + 1, r]
    30. int mid = l + (r - l) / 2;
    31. buildSegTree(leftChild(treeIndex), l, mid);
    32. buildSegTree(rightChild(treeIndex), mid + 1, r);
    33. tree[treeIndex] = merger.merge(tree[leftChild(treeIndex)], tree[rightChild(treeIndex)]);
    34. }
    35. // 区间查询
    36. public E Query(int QueryL, int QueryR){
    37. if(QueryL < 0 || QueryL >= data.length || QueryR < 0 || QueryR >=data.length){
    38. throw new IllegealmentException(" out of index");
    39. }
    40. E res = Query(0 , 0, data.length, QueryL, QueryR);
    41. return res;
    42. }
    43. // 从 root 开始,在 [0, data.length - 1] 中查询 QueryL ~ QueryR
    44. private E query(int treeIndex, int l, int r, int QueryL, int QueryR){
    45. if(l == QueryL && r == QueryR)
    46. return tree[treeIndex];
    47. int mid = l + (r - l) / 2;
    48. if(QueryR <=mid){
    49. return query(leftChild(treeIndex), l, mid, QueryL ,Query R);
    50. }else if(QueryL >= mid + 1){
    51. return query(rightChild(treeIndex), mid+1, r, QueryL, QueryR);
    52. }else{ // QueryL <= mid && QueryR >= mid
    53. return merger.merge(query(leftChild(treeIndex), l, mid, QueryL, mid), query(rightChild(treeIndex), mid+1, r, mid + 1, r));
    54. }
    55. }
    56. // 更改数值,更改数组某index 的元素
    57. public void set(int index, E value){
    58. if(index < 0 || index >= data.length){
    59. throw new IllegalException("out of index");
    60. }
    61. set(0, 0, data.length - 1, index, value);
    62. }
    63. public void set(int treeIndex, int l, int r, int index, E value){
    64. if(l == r){
    65. tree[treeIndex] = value;
    66. }
    67. int mid = l + (r - l) / 2;
    68. if(index >= mid + 1){
    69. set(rightChild(treeIndex), mid + 1, r, index, value);
    70. }else if(index <= mid){
    71. set(leftChild(treeIndex), l, mid, index, value);
    72. }
    73. tree[treeIndex] = merger.merge(tree[leftchild(treeIndex)], tree[rightchild(treeIndex)]);
    74. }
    75. }

4.线段树的区间更新:

  • 最好使用lazy[] 区间记录未更新内容,等下一次需要更新的时候,再把lazy[] 传入更新,要不频繁更新开销太大。

    5.线段树的练习:

    leetcode 303, leetcode 307