首页 > 其他分享 >线段树:线段树的定义和应用

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

时间:2024-08-11 13:57:36浏览次数:13  
标签:node end 定义 int 线段 tree start 应用

引言

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

目录

  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博客,点赞并收藏这篇文章,您的支持是我持续创作的动力!


关键内容总结

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

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


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

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

标签:node,end,定义,int,线段,tree,start,应用
From: https://blog.csdn.net/qq_40254606/article/details/141038777

相关文章

  • 数组应用实例-三子棋
    目录1.文件组织2.test.c文件的架构2.1主函数2.2菜单2.3游戏2.3.1棋盘初始化:2.3.2下棋过程2.3.3判断输赢3.具体函数声明与实现3.1初始化棋盘3.2打印棋盘3.3玩家下棋3.4判断输赢3.5棋盘占满3.6电脑下棋4.最后调整1.文件组织采用多文件组......
  • 动手做科研-day01-AI的最新进展与科研应用
    01.Python程序运行工具以及环境搭建选择使用kaggle官方的notebook作为环境搭建的平台,因为之前使用过kaggle进行注册,因此直接简单登录,按照下图依次进行操作note:需要挂来登录1.点击create2.创建notebook记事本3.尝试写一个简单的helloworld先新建codeprint("hell......
  • 【系统分析师论文】论企业应用集成
    论企业应用集成前沿论文题目摘要正文前沿本人参加软考培训,已通过软考拿到高级工程师职称,故分享给大家论文的原稿,每篇论文都是经过培训机构老师批改过,可以学习借鉴论文的框架和分段方式,非常实用。论文题目摘要2020年5月,我参与了某数字化车间管理系统的研发与......
  • 【系统分析师论文】论系统自动化测试及其应用
    论系统自动化测试及其应用前沿论文题目摘要正文前沿本人参加软考培训,已通过软考拿到高级工程师职称,故分享给大家论文的原稿,每篇论文都是经过培训机构老师批改过,可以学习借鉴论文的框架和分段方式,非常实用。论文题目摘要2020年5月,我参与了某数字化车间管理系......
  • DLMS/COSEM中的信息安全:应用加密保护(上)
    1.保护xDLMSAPDU1.1综述    本节介绍对称加密算法和非对称加密算法用于保护xDLMSAPDU。        ——1.2规定了安全策略和访问权限的可能值;        ——1.3显示了加密APDU的类型;        ——1.4规定了使用AES-GCM算法进行认证和加密; ......
  • DLMS/COSEM中的信息安全:应用加密保护(下)
    2.多方多层保护     加密保护可以由多方应用。通常多方是:    ——服务器;    ——客户机;    ——第三方;    每一方可以应用一层或多层保护:    ——应用加密,认证或认证加密,使用加密APDU。第三方应使用通用加密APDU。客户......
  • Pollard-Rho的一些应用
    P4718https://www.luogu.com.cn/problem/P4718要求找最大的素因子,考虑可能出现在因子的因子中,所以需要递归i64max_prime(i64n){if(isp(n)){returnn;}i64mx{std::numeric_limits<i64>::min()};while(n!=1){autodiv{findDiv(n)};mx......
  • 掌握 Nuxt 3 的页面元数据:使用 definePageMeta 进行自定义配置
    title:掌握Nuxt3的页面元数据:使用definePageMeta进行自定义配置date:2024/8/11updated:2024/8/11author:cmdragonexcerpt:摘要:本文详细介绍Nuxt3框架中definePageMeta的使用方法,包括如何为页面组件定义元数据,如布局、过渡效果、路由中间件等。通过具体示例展示......
  • 通信编码揭秘:(二)信道编码(汉明码、循环冗余校验码、里德所罗门码)与其应用
    通信编码揭秘:2.信道编码(汉明码、循环冗余校验码、里德所罗门码)与其应用摘要信道编码的目的是提高数据传输的可靠性,确保即使在噪声环境下传输的数据也能被正确接收。本文将探讨汉明码、循环冗余校验(CRC)和里德-所罗门码三种常见的信道编码方法,并通过实际例子说明它们的应用......
  • COMSOL Multiphysics软件二次开发:COMSOL软件在流体力学中的应用
    COMSOLMultiphysics软件二次开发:COMSOL软件在流体力学中的应用COMSOLMultiphysics概述COMSOLMultiphysics是一款强大的多物理场仿真软件,它允许用户通过图形用户界面(GUI)或通过二次开发接口(即COMSOLAPI)来建立和求解复杂的物理模型。COMSOL软件的核心优势在于其能够......