• Java线段树


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

    堆也是平衡二叉树

    堆满二叉树:

    h层,一共有2^h-1个节点(大约是2^h)

    最后一层(h-1层)有2^(h-1)个节点

    最后一层的节点数大致等于前面所有层节点之和

    如果区间有n个元素,数组表示需要4n的空间

    不考虑添加元素,使用4n的静态空间即可

    接口:

    public interface Merger<E> {
        E merge(E a, E b);
    }
    

      

    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, arr.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 = rightChild(treeIndex);
    
            // int mid = (l + r) / 2;
            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];
        }
    
        // 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引
        private int leftChild(int index){
            return 2*index + 1;
        }
    
        // 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引
        private int rightChild(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 class Main {
    
        public static void main(String[] args) {
    
            Integer[] nums = {-2, 0, 3, -5, 2, -1};
    //        SegmentTree<Integer> segTree = new SegmentTree<>(nums,
    //                new Merger<Integer>() {
    //                    @Override
    //                    public Integer merge(Integer a, Integer b) {
    //                        return a + b;
    //                    }
    //                });
    
            SegmentTree<Integer> segTree = new SegmentTree<>(nums,
                    (a, b) -> a + b);
            System.out.println(segTree);
        }
    }
    

      查找方法:

    // 返回区间[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);
        }
    
        // 在以treeIndex为根的线段树中[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;
            // treeIndex的节点分为[l...mid]和[mid+1...r]两部分
    
            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);
    
            E leftResult = query(leftTreeIndex, l, mid, queryL, mid);
            E rightResult = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);
            return merger.merge(leftResult, rightResult);
        }
    

      

    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(0, 2));
    }
    }
    

      

    线段树添加:

    public class NumArray {
    
    	private SegmentTree<Integer> segmentTree;
    	public NumArray(int[] nums){
    		if(nums.length>0){
    			Integer[] data=new Integer[nums.length];
    			for(int i=0;i<nums.length;i++)
    				data[i]=nums[i];
    			segmentTree =new SegmentTree<>(data, (a,b)->a+b);
    		}
    	}
    	public int sumRange(int i,int j){
    		if(segmentTree==null)
    			throw new IllegalArgumentException("Segment Tree is null");
    		return segmentTree.query(i, j);
    	}
    }
    

      不用线段树添加:

    public class NumArray2 {
    	 private int[] sum;//sum[i]存储前i个元素和 sum[0]=0
         public NumArray2(int[] nums){
        	 sum=new int[nums.length+1];
        	 sum[0]=0;
        	 for(int i=01;i<sum.length;i++)
        		 sum[i]=sum[i-1]+nums[i-1];
         }
    	 public int sumRange(int i,int j){
    		 return sum[j+1]-sum[i];
    	 }
    }
    

      不用线段树修改:(效率很低)

    public class NumArray3 {
        private int[] sum;
        private int[] data;
        public NumArray3(int[] nums){
        	data=new int[nums.length];
        	for(int i=0;i<data.length;i++)
        		data[i]=nums[i];
        	sum=new int[nums.length+1];
        	sum[0]=0;
        	for(int i=1;i<sum.length;i++)
        		sum[i]=sum[i-1]+nums[i-1];
        }
        public void update(int index,int val){
        	data[index]=val;
        	for(int i=index+1;i<sum.length;i++)
        		sum[i]=sum[i-1]+data[i-1];
        }
        public int sumRange(int i,int j){
        	return sum[j+1]-sum[i];
        }
    }
    

      用线段树更新:

    //将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=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]);
        }
    

      

    public void update(int index,int val){
    		if(segmentTree==null)
    			throw new IllegalArgumentException("Segment Tree is null");
    		segmentTree.set(index,val);
    	}
    

      

  • 相关阅读:
    SpringRequestContext源码阅读
    MyBatis事务管理源码阅读
    linux查找依赖文件
    GitHub
    Qt Quick
    centos7下安装chrome
    软件使用
    排序算法之冒泡排序
    c++学习
    cent6.4使用
  • 原文地址:https://www.cnblogs.com/sunliyuan/p/10720167.html
Copyright © 2020-2023  润新知