線段樹

線段樹

  • 線段樹是一種二叉搜索樹,與區間樹相似,它將一些區間劃分爲一些單元區間,每一個單元區間對應線段樹中的一個葉子節點,對於線段樹中的每一個非葉子節點[a,b],它的左兒子表示的區間爲[a,(a+b)/2],右兒子表示的區間爲[(a+b)/2+1,b]。因此線段樹是平衡二叉樹,最後的子節點數目爲N,即整個線段區間的長度。使用線段樹可以快速的查找某一個節點在若干條線段中出現的次數,時間複雜度爲O(logN)。而未優化的空間複雜度爲2N,因此有時需要離散化讓空間壓縮。

  • 既不是滿二叉樹,也不是完全二叉樹

  • 如果區間有n個元素,那麼我們的線段樹不考慮添加元素,即區間固定,使用4n的靜態空間即可

  • 線段樹是有限個元素的數組進行區間表達,而不是我們意義上的數軸的線段

  • 代碼:

    package 數據結構.SegmentTree;
    
    import javax.management.Query;
    import javax.swing.tree.TreeCellRenderer;
    
    public class SegmentTree<E> {
        private E[] data;
        private E[] tree;
        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);
        }
        //再treeIndex的位置創建表示區間[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 = rigthChild(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 int getSize()
        {
            return data.length;
        }
        public E get(int index){
            if(index<0||index>=data.length)
                throw new IllegalArgumentException("index is illegal...");
            return data[index];
        }
        //查詢queryl,queryr的值,符合線段左右的值
        public E query(int queryL,int queryR)
        {
            if(queryL<0||queryL>=data.length||queryR<0||queryR>=data.length||queryL>queryR)
                throw new IllegalArgumentException("Index is illegal.");
            return query(0,0,data.length-1,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 = rigthChild(treeIndex);
            if(queryL>=mid+1)
                return query(rightTreeIndex,mid+1,r,queryL,queryR);
            else if(queryR<=mid)
                return query(leftTreeIndex,l,mid,queryL,queryR);
            E leftResult = query(leftTreeIndex,l,mid,queryL,mid);
            E rightResult = query(rightTreeIndex,mid+1,r, mid+1,queryR);
            return merger.merge(leftResult,rightResult);
        }
        //返回完全二叉樹的數組中,一個索引表示的元素的左孩子節點的索引
        private int leftChild(int index){
            return 2*index+1;
        }
        //返回完全二叉樹的數組中,一個索引表示的元素的右孩子節點的索引
        private int rigthChild(int index){
            return  2*index+2;
        }
    
        @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();
        }
    }
    public interface Merger<E> {
        E merge(E a,E b);
    }
    package 數據結構.SegmentTree;
    
    public class Main {
        public static void main(String[] args) {
            Integer[] nums = {-2,0,3,-5,2,-1};
            SegmentTree<Integer> segTree = new SegmentTree<>(nums,(a, b) -> a+b);
            System.out.println(segTree.query(2,5));
        }
    }
    
    
  • 爲線段樹添加更新操作:首先得找到對應的索引位置的節點值進行修改,同時,還要注意的是不僅僅需要對索引進行修改,同時還需要不斷的更新操作

        //將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);
        }
        //在以treeIndex爲根的線段樹中更新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 = rigthChild(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]);
        }
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章