线段树:固定区间动态变化

Leetcode 715. Range 模块

Leetcode 307. 区域和检索 - 数组可修改

Leetcode 933. 最近的请求次数

Leetcode 732. 我的日程安排表 III

Leetcode 699. 掉落的方块

        以上是 Leetcode 中与线段树有关的题目

        线段树解决的是求 区间问题 ,且该 区间 会被修改 

        线段树的特点:每个节点代表一个区间

 

Leetcode 307 区域和检索 - 数组可修改(区间大小固定)

        使用数组模拟树 ,因为 nums 数组的大小是固定的,而树的容量大小固定为4倍的数组大小

        当前节点在数组中的位置 curPos,左子树节点位置 curPos*2+1,右子树节点位置 curPos*2+2

        当前节点表示的区间  [ curLeft , curRight ] ,区间中间点 mid = curLeft + ( curRight - curLeft ) / 2 ,则当前节点的左子树的区间为   [ curLeft , mid ] ,右子树的区间为  [ mid + 1 , curRight ]

        线段树的两种操作:

        1. 更新操作:(每个节点代表一个区间)代入当前节点 根节点 curPos = 0,当前节点表示的区间 [ curLeft , curRight ] ;

                向下寻找到需要更新的目标节点,目标节点表示的区间为 [ index , index ]

                如果当前节点的区间 curLeft = curRight ,表示找到目标节点区间,更新目标节点

                如果目标区间 index 小于等于当前节点左子树,往左子树上继续寻找;否则,往右子树上继续寻找

                更新后,向上回溯,更新当前节点

        2. 区间检索:(每个节点代表一个区间)代入当前节点 根节点 curPos = 0,当前节点表示的区间 [ curLeft , curRight ] ;

                向下寻找到所求的目标区间,目标区间为 [ left , right ]

                如果目标区间与当前节点所代表的区间一致,返回当前节点保存的结果;

                如果目标区间 right 小于等于当前节点左子树,往左子树上继续寻找;

                如果目标区间 left 大于当前节点右子树,往右子树上继续寻找;

                如果目标区间在包含在两个子树之间,求往左子树和右子树上继续寻找的和

class NumArray2 {
    int[] tree;
    int len;
    
    // 初始化 构造线段树
    public NumArray2(int[] nums) {
        len = nums.length;
        tree = new int[len * 4];
        buildTree(0, 0, len - 1, nums);
    }

    public void update(int index, int val) {
        change(index, val, 0, 0, len - 1);
    }

    public int sumRange(int left, int right) {
        return sum(left, right, 0, 0, len - 1);
    }
    
    public int sum(int left, int right, int curPos, int curLeft, int curRight) {
        if (left == curLeft && right == curRight) {
            return tree[curPos];
        }
        int mid = curLeft + (curRight - curLeft) / 2;
        if (right <= mid) {
            return sum(left, right, curPos * 2 + 1, curLeft, mid);
        } else if (left > mid) {
            return sum(left, right, curPos * 2 + 2, mid + 1, curRight);
        } else {
            return sum(left, mid, curPos * 2 + 1, curLeft, mid) + sum(mid + 1, right, curPos * 2 + 2, mid + 1, curRight);
        }

    }
    // 更新节点 
    public void change(int index, int val, int curPos, int curLeft, int curRight) {
        if (curLeft == curRight) {
            tree[curPos] = val;
            return;
        }
        int mid = curLeft + (curRight - curLeft) / 2;
        if (index <= mid) {
            change(index, val, curPos * 2 + 1, curLeft, mid);
        } else {
            change(index, val, curPos * 2 + 2, mid + 1, curRight);
        }
        tree[curPos] = tree[curPos * 2 + 1] + tree[curPos * 2 + 2];
    }

    public void buildTree(int curPos, int left, int right, int[] nums) {
        if (left == right) {
            tree[curPos] = nums[left];
            return;
        }
        int mid = left + (right - left) / 2;
        buildTree(curPos * 2 + 1, left, mid, nums);
        buildTree(curPos * 2 + 2, mid + 1, right, nums);
        // 后续遍历, 当前节点为左右子树的和
        tree[curPos] = tree[curPos * 2 + 1] + tree[curPos * 2 + 2];
    }

}




参考来源:线段树详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值