• java——线段树 SegmentTree


    应用:

      区间染色

      区间查询

    线段树不是完全二叉树,线段树是平衡二叉树

    使用数组来实现线段树:存储空间为4n

    以下是使用数组实现的静态线段树:

    public class SegmentTree<E> {
        
        private E[] tree;
        private E[] data;
        private Merger<E> merger;
         
        public SegmentTree(E[] arr, Merger<E> merger) {
            this.merger = merger;
            data = (E[]) new Object[arr.length];
            for(int i = 0 ; i < arr.length ; i ++) {
                data[i] = arr[i];
            }
            
            tree = (E[]) new Object[4 * arr.length];
            buildSegmentTree(0, 0, data.length - 1);
        }
        //在tree Index的位置创建表示区间[l ... r]的线段树
        private void buildSegmentTree(int treeIndex, int l, int r) {
            
            if(l == r) {
                tree[treeIndex] = data[l];
                return;
            }
            
            int leftTreeIndex = leftChild(treeIndex);
            int rightTreeIndex = rightChild(treeIndex);
            int mid = l + (r - l) / 2;
            buildSegmentTree(leftTreeIndex, l, mid);
            buildSegmentTree(rightTreeIndex, mid + 1, r);
            
            //根据业务组合线段树
            tree[treeIndex] = merger.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
        }
        
        
        public E get(int index) {
            if(index < 0 || index >= data.length) {
                throw new IllegalArgumentException("Index is illegal.");
            }
            return data[index];
        }
        
        public int getSize() {
            return data.length;
        }
        
        private int leftChild(int index) {
            return 2*index + 1;
        }
        
        private int rightChild(int index) {
            return 2*index + 2;
        }
        
        public E query(int queryL, int queryR) {
            if(queryL < 0 || queryL >= data.length || queryR < queryL){
                throw new IllegalArgumentException("Index is illegal.");    
            }
            return query(0, 0, data.length -1, queryL, queryR);
        }
        //查询线段树
        //在以treeID为根的线段树中[l...r]的范围里,搜索区间[queryL...queryR]的值
        private E query(int treeIndex, int l, int r, int queryL, int queryR) {
            if(l == queryL && r == queryR) {
                return tree[treeIndex];
            }
            
            int mid = l + (r-l)/2;
            int leftTreeIndex = leftChild(treeIndex);
            int rightTreeIndex = rightChild(treeIndex);
            
            if(queryL >= mid +1) {
                return query(rightTreeIndex, mid + 1, r, queryL, queryR);
            }else if(queryR <= mid) {
                return query(leftTreeIndex, l, mid, queryL, queryR);
            }else {
                //这种情况下产生了两段线段树,需要进行融合
                E leftResult = query(leftTreeIndex, l, mid, queryL, mid);
                E rightResult = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);
                return merger.merge(leftResult, rightResult);
            }
        }
        //将index位置的值更新为e
        public void set(int index, E e) {
            if(index < 0 || index >= data.length) {
                throw new IllegalArgumentException("Index is illegal");
            }
            data[index] = e;
            set(0,0, data.length - 1, index, e);
        }
        
        private void set(int treeIndex, int l, int r, int index, E e) {
            if(l == r) {
                tree[treeIndex] = e;
                return;
            }
            
            int mid = l + (r - l) / 2;
            int leftTreeIndex = leftChild(treeIndex);
            int rightTreeIndex = rightChild(treeIndex);
            if(index >= mid + 1) {
                set(rightTreeIndex, mid + 1, r, index, e);
            }else {
                set(leftTreeIndex, l, mid, index, e);
            }
            
            tree[treeIndex] = merger.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
        }
        
        @Override
        public String toString() {
            StringBuilder res = new StringBuilder();
            res.append('[');
            for(int i = 0 ; i < tree.length ; i ++) {
                if(tree[i] != null) {
                    res.append(tree[i]);
                }else {
                    res.append("null");
                }
                if(i != tree.length - 1) {
                    res.append(", ");
                }
            }
            res.append("]");
            return res.toString();
        }
        
    }

     对于一个区间的更新:

      懒惰更新:使用lazy数组记录未更新的内容,下一次访问时先访问lazy数组,若有内容,更新后再访问即可。

    动态线段树:

      使用链表实现

      节省空间

      可以不均等划分区间,便于实际应用

  • 相关阅读:
    形态学操作
    形态学腐蚀膨胀操作
    图像模糊操作
    OpenCV像素操作和图形绘制
    c++中char类型的取值范围
    二叉树基本操作
    剑指27 二叉树的镜像
    剑指26 树的子结构
    剑指24: 反转链表
    剑指22 链表倒数第k个节点
  • 原文地址:https://www.cnblogs.com/gaoquanquan/p/9881294.html
Copyright © 2020-2023  润新知