线段树--Segment Tree

数据结构的特点

  1. 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
  2. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。
  3. 因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。

图例说明

这里写图片描述

代码实现(以求区间和为例)

构建Segment Tree
/**
     * 定义树节点
     */
    private static class TreeNode {
        public int start;
        public int end;
        public int num;
        public TreeNode left;
        public TreeNode right;

        public TreeNode (int start,int end) {
            this.start = start;
            this.end = end;
        }

        public TreeNode (int start,int end,int num) {
            this(start, end);
            this.num = num;
        }
    }
    /**
     * 构建segment tree
     * @param arr
     * @return
     */
    public static TreeNode buildTree(int[] arr) {
        if (arr == null || arr.length == 0) {
            return null;
        }

        return buildTree(arr,0,arr.length-1);
    }

    /**
     * 采用递归的方式构建树
     * @param arr
     * @param start
     * @param end
     * @return
     */
    private static TreeNode buildTree(int[] arr,int start,int end) {
        TreeNode node = null;
        if (end == start) {
            node = new TreeNode(start,start,arr[start]);
        }
        else {
            int mid = start + (end -start) /2;
            node = new TreeNode(start,end);
            node.left = buildTree(arr,start,mid);
            node.right = buildTree(arr,mid+1,end);
            node.num = node.left.num + node.right.num;
        }
        return node;
    }

构建线段树的时间复杂度:O(n);

查询区间树的值

递归的查询区间树。

private static int querySum(TreeNode node,int start,int end) {
        /**
         * 节点为==null,返回Null
         */
        if (node == null) {
            return 0;
        }
        int nStart = node.start;
        int nEnd = node.end;
        /**
         * 如果左右区间相等,则返回值
         */
        if (nStart == start && nEnd == end) {
            return node.num;
        }

        /**
         * 获取区间中间值
         */
        int nMid = nStart + (nEnd-nStart) / 2;
        /**
         * 左区间的[start,mid]
         */
        if (nMid >= end) {
            return querySum(node.left,start,end);
        }
        /**
         * 右区间[mid+1,end]
         */
        else if (nMid < start) {
            return querySum(node.right,start,end);
        }
        else if (nMid >= start && nMid < end) {
            return querySum(node.left,start,nMid) + querySum(node.right,nMid+1,end); 
        }
        return 0;
    }

查询时间复杂度:O(logn)

线段树的更新

往数组中添加元素,删除元素会让整个线段树发生改变。因此不建议添加或者删除元素。
但可以更新数组中元素的值。

  • 计算出需要更新元素的差值:diff =新值-旧值
  • 从更元素开始更新元素的sum值: sum = sum + diff
  • 依据更新元素的index,递归的更新左子树或者右子树,直到更新到叶子节点。
private static void updateTreeNode(TreeNode node,int index,int diff) {
        int start = node.start;
        int end = node.end;
        int mid = start + (end - start) / 2;
        node.num = node.num + diff;
        /**
         * 左右区间相等,说明是叶子节点
         */
        if (start == end) {
            return;
        }
        /**
         * 如果更新的index in [start,mid]
         */
        else if (index <= mid) {
            updateTreeNode(node.left,index,diff);
        }
        /**
         * 如果更新的index in [mid+1,end]
         */
        else if (index > mid) {
            updateTreeNode(node.right,index,diff);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值