线段树:线段树的定义和应用

引言

线段树是一种高级数据结构,用于解决区间查询和更新问题。它在许多应用中都有广泛的使用,例如数组区间的求和、最小值/最大值查询、区间的最小公倍数/最大公约数查询等。本文将详细介绍线段树的定义、构建、应用以及代码实现。

目录

  1. 线段树的定义
  2. 线段树的应用
  3. 线段树的构建
  4. 线段树的操作
  5. 线段树的实现

线段树的定义

线段树(Segment Tree)是一种二叉树,用于存储数组区间的信息。每个节点代表一个区间,存储该区间的一些信息,例如区间和、区间最小值或最大值。线段树具有以下性质:

  • 每个叶子节点表示数组的一个元素。
  • 每个非叶子节点表示其子节点所表示区间的并集。
  • 线段树的高度为 (O(\log n)),其中 (n) 是数组的长度。

线段树的应用

线段树主要用于以下应用场景:

  1. 区间查询:如区间和查询、区间最小值/最大值查询。
  2. 区间更新:如单点更新、区间更新。
  3. 其他复杂查询:如区间的最小公倍数/最大公约数查询、区间的差值等。

线段树的构建

线段树的构建过程包括以下步骤:

  1. 初始化:根据数组长度确定线段树的大小,并初始化线段树数组。
  2. 构建节点:递归构建线段树节点,每个节点存储对应区间的信息。

线段树的操作

查询操作

查询操作用于在给定的区间内查询一些信息,例如区间和、区间最小值/最大值。查询操作的时间复杂度为 (O(\log n))。

更新操作

更新操作用于修改数组中的某个元素或某个区间的值,并更新线段树中相关节点的信息。更新操作的时间复杂度为 (O(\log n))。

线段树的实现

代码实现

下面是用Java实现线段树的代码示例:

public class SegmentTree {
    private int[] tree;
    private int[] nums;
    private int n;

    public SegmentTree(int[] nums) {
        this.nums = nums;
        this.n = nums.length;
        this.tree = new int[4 * n];
        buildTree(0, 0, n - 1);
    }

    private void buildTree(int node, int start, int end) {
        if (start == end) {
            tree[node] = nums[start];
        } else {
            int mid = (start + end) / 2;
            int leftChild = 2 * node + 1;
            int rightChild = 2 * node + 2;
            buildTree(leftChild, start, mid);
            buildTree(rightChild, mid + 1, end);
            tree[node] = tree[leftChild] + tree[rightChild]; // 区间和
        }
    }

    public void update(int idx, int val) {
        updateTree(0, 0, n - 1, idx, val);
    }

    private void updateTree(int node, int start, int end, int idx, int val) {
        if (start == end) {
            nums[idx] = val;
            tree[node] = val;
        } else {
            int mid = (start + end) / 2;
            int leftChild = 2 * node + 1;
            int rightChild = 2 * node + 2;
            if (idx <= mid) {
                updateTree(leftChild, start, mid, idx, val);
            } else {
                updateTree(rightChild, mid + 1, end, idx, val);
            }
            tree[node] = tree[leftChild] + tree[rightChild]; // 区间和
        }
    }

    public int query(int L, int R) {
        return queryTree(0, 0, n - 1, L, R);
    }

    private int queryTree(int node, int start, int end, int L, int R) {
        if (L > end || R < start) {
            return 0; // 无效区间
        }
        if (L <= start && end <= R) {
            return tree[node];
        }
        int mid = (start + end) / 2;
        int leftChild = 2 * node + 1;
        int rightChild = 2 * node + 2;
        int leftSum = queryTree(leftChild, start, mid, L, R);
        int rightSum = queryTree(rightChild, mid + 1, end, L, R);
        return leftSum + rightSum;
    }

    public static void main(String[] args) {
        int[] nums = {1, 3, 5, 7, 9, 11};
        SegmentTree segmentTree = new SegmentTree(nums);

        System.out.println("初始区间和查询(0, 5): " + segmentTree.query(0, 5));
        segmentTree.update(1, 10);
        System.out.println("更新后区间和查询(0, 5): " + segmentTree.query(0, 5));
    }
}

代码解释

  1. 初始化线段树

    public SegmentTree(int[] nums) {
        this.nums = nums;
        this.n = nums.length;
        this.tree = new int[4 * n];
        buildTree(0, 0, n - 1);
    }
    

    初始化线段树,创建大小为4倍数组长度的线段树数组,并调用 buildTree 方法构建线段树。

  2. 构建线段树

    private void buildTree(int node, int start, int end) {
        if (start == end) {
            tree[node] = nums[start];
        } else {
            int mid = (start + end) / 2;
            int leftChild = 2 * node + 1;
            int rightChild = 2 * node + 2;
            buildTree(leftChild, start, mid);
            buildTree(rightChild, mid + 1, end);
            tree[node] = tree[leftChild] + tree[rightChild]; // 区间和
        }
    }
    

    递归构建线段树,叶子节点存储数组元素值,非叶子节点存储其子节点区间的和。

  3. 更新操作

    public void update(int idx, int val) {
        updateTree(0, 0, n - 1, idx, val);
    }
    
    private void updateTree(int node, int start, int end, int idx, int val) {
        if (start == end) {
            nums[idx] = val;
            tree[node] = val;
        } else {
            int mid = (start + end) / 2;
            int leftChild = 2 * node + 1;
            int rightChild = 2 * node + 2;
            if (idx <= mid) {
                updateTree(leftChild, start, mid, idx, val);
            } else {
                updateTree(rightChild, mid + 1, end, idx, val);
            }
            tree[node] = tree[leftChild] + tree[rightChild]; // 区间和
        }
    }
    

    更新数组中的某个元素,并递归更新线段树中的相关节点。

  4. 查询操作

    public int query(int L, int R) {
        return queryTree(0, 0, n - 1, L, R);
    }
    
    private int queryTree(int node, int start, int end, int L, int R) {
        if (L > end || R < start) {
            return 0; // 无效区间
        }
        if (L <= start && end <= R) {
            return tree[node];
        }
        int mid = (start + end) / 2;
        int leftChild = 2 * node + 1;
        int rightChild = 2 * node + 2;
        int leftSum = queryTree(leftChild, start, mid, L, R);
        int rightSum = queryTree(rightChild, mid + 1, end, L, R);
        return leftSum + rightSum;
    }
    

    查询给定区间的和,递归查找并合并子区间的结果。

  5. 主函数

    public static void main(String[] args) {
        int[] nums = {1, 3, 
    
    

5, 7, 9, 11};
SegmentTree segmentTree = new SegmentTree(nums);

    System.out.println("初始区间和查询(0, 5): " + segmentTree.query(0, 5));
    segmentTree.update(1, 10);
    System.out.println("更新后区间和查询(0, 5): " + segmentTree.query(0, 5));
}
```
创建线段树并进行区间查询和更新操作。

图解说明

以下是线段树构建和查询过程的图解:

区间和
26
9
17
4
5
16
11
1
3
7
9
数组: 1, 3, 5, 7, 9, 11
构建线段树
查询区间和 0, 5
从根节点 0, 5 开始
查询左子树 0, 2
查询右子树 3, 5
合并结果
合并结果
结果: 9
结果: 17
总和: 26

结论

通过上述讲解和实例代码,我们详细展示了线段树的定义、构建、应用以及操作。线段树是一种强大的数据结构,能够高效地处理区间查询和更新操作,适用于多种场景。希望这篇博客对您有所帮助!


如果您觉得这篇文章对您有帮助,请关注我的CSDN博客,点赞并收藏这篇文章,您的支持是我持续创作的动力!


关键内容总结

  • 线段树的定义和应用
  • 线段树的构建和操作
  • 线段树的代码实现和图解说明

推荐阅读:深入探索设计模式专栏,详细讲解各种设计模式的应用和优化。点击查看:深入探索设计模式


特别推荐:设计模式实战专栏,深入解析设计模式的实际应用,提升您的编程技巧。点击查看:设计模式实战

如有任何疑问或建议,欢迎在评论区留言讨论。谢谢阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

捕风捉你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值