我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java数据结构之线段树的原理与实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java数据结构之线段树的原理与实现

简介

线段树是一种二叉搜索树,是用来维护区间信息的数据结构。可以在O(logN)的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间最大值,求区间最小值)等操作。接下来我以实现区间求和为例子来讲解线段树(最大值和最小值与求和实现方式几乎无异),假设存在一个数组[1,4,6,3,9]。

实现思路

从线段树的定义,我们首先需要定义一个树节点,节点包含区间和(23),区间([1-5]),左节点,右节点等。(如果要实现求区间最大值,最小值,则还需包含这些)。然后需要提供构建线段树,线段树支持修改节点操作方法。

节点定义

@Data
public static class Node {
   //区间起始下标
   private int start;
   //区间结尾下标
   private int end;
   //当前区间和值
   private int value;
   private Node left;
   private Node right;
   Node(int start, int end, int value) {
       this.start = start;
       this.end = end;
       this.value = value;
  }
}

构建线段树

因为构建线段树时候需要计算当前区间和,所以我们可以先初始化一个前缀和数组,在构建线段树时候利用下标快速计算出区间和,同时为了保证每个节点有一致的操作,初始化一个头节点,指向root(这是链表树等常用的简化操作的方法)

//head 指向线段树root节点的指针,使得root节点与其余节点操作保持一致
   Node head;
   int size;
   List<Integer> nums;

   //前缀和数组,便于构建线段树时候计算区间值,用于初次构建辅助
   List<Integer> prefixSum ;
   public void init(List<Integer> nums) {
       //初始化一个头节点,便于操作
       this.head = new Node(-1, -1, -1);
       this.nums = nums;
    //初始化前缀和数组
       prefixSum = new ArrayList<>(nums.size());
       prefixSum.add(0);
       for (int i = 0; i < nums.size() ; i++) {
           prefixSum.add(prefixSum.get(prefixSum.size() - 1) + nums.get(i));
      }
    //构建线段树
       this.build(1, nums.size());
       size = nums.size();
  }

   //构建线段树
   public void build(int start, int end) {
      Node root = new Node(start, end, prefixSum.get(end) - prefixSum.get(start - 1));
     //将头节点右子树指向root
      head.right = root;
     //从root开始构建线段树
      this.madeChild(root, start, end);
  }
private void madeChild(Node node, int start, int end) {
       if (start >= end) {
           return;
      }
       //分个左右子树,左子树取start~mid,右子树取mid+1~end
       int mid = start + ((end - start) >> 1);
       if (start <= mid) {
           Node left = new Node(start, mid, prefixSum.get(mid) - prefixSum.get(start - 1));
           node.left = left;
           madeChild(left, start, mid);
      }
       if (mid + 1 <= end) {
           Node right = new Node(mid + 1, end, prefixSum.get(end) - prefixSum.get(mid));
           node.right = right;
           madeChild(right, mid + 1, end);
      }
  }

求解区间和

求解区间和过程就是遍历线段树,将求解区间与当前节点区间进行比较,如果全部存在于左子树或者右子树,则直接深度继续在左子树右子树遍历即可,但是如果求解区间在当前节点的左右子树均有部分,则需要将当前区间分为两个部分,然后分别深度遍历左右子树,最后将结果相加。

//求解区间和
   public int findSectionSum(int start, int end) {
       //深度遍历线段树,找到对应区间
       if (start < 1 || end > size || start > end) {
           return -1;
      }
       return dfsFindSectionSum(head.right, start, end);
  }    

   private int dfsFindSectionSum(Node node, int start, int end) {
       if (node.start == start && node.end == end) {
           //找到区间
           return node.value;
      }
       if (this.isContain(node.left.start, node.left.end, start, end)) {
           //在左子树中
           return this.dfsFindSectionSum(node.left, start, end);
      }
       if (this.isContain(node.right.start, node.right.end, start, end)) {
           //包含在右子树中
           return this.dfsFindSectionSum(node.right, start, end);
      }
       //左边一部分,右边一部分
       return this.dfsFindSectionSum(node.left, start, node.left.end) + this.dfsFindSectionSum(node.right, node.right.start, end);
  }
   
   private boolean isContain(int start1, int end1, int start2, int end2){
       return start2 >= start1 && end2 <= end1;
  }

更新线段树

当更新指定位置元素的值的时候,我们需要将线段树中区间包含该节点的区间和进行更新。我们可以从根节点开始深度遍历线段树,如果当前节点包含该位置,我们更新区间和,然后根据当前节点左右子节点的区间,判断走左子树还是右子树,直至更新到叶子节点,则更新完成。

//更新线段树,将index位置的值更新为value,需要更新沿路的值
   public void update(int index, int value) {
       Node root = head.right;
       while (null != root) {
           if (index >= root.start && index <= root.end) {
               root.value += value - nums.get(index - 1);
          }
           int mid = root.start + ((root.end - root.start) >> 1);
           if (index <= mid) {
               root = root.left;
               continue;
          }
           root = root.right;
      }
       nums.set(index - 1, value);
  }

以上就是Java数据结构之线段树的原理与实现的详细内容,更多关于Java 线段树的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java数据结构之线段树的原理与实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java数据结构之线索化二叉树怎么实现

这篇文章主要介绍“Java数据结构之线索化二叉树怎么实现”,在日常操作中,相信很多人在Java数据结构之线索化二叉树怎么实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java数据结构之线索化二叉树怎么实现
2023-06-30

Java数据结构之双端链表原理与实现方法

本文实例讲述了Java数据结构之双端链表原理与实现方法。分享给大家供大家参考,具体如下:一、概述:1、什么时双端链表:链表中保持这对最后一个连点引用的链表2、从头部插入要对链表进行判断,如果为空则设置尾节点为新添加的节点3、从尾部进行插入如
2023-05-30

C++数据结构之红黑树的实现

红黑树在表意上就是一棵每个节点带有颜色的二叉搜索树,并通过对节点颜色的控制,使该二叉搜索树达到尽量平衡的状态。本文主要为大家介绍了C++中红黑树的原理及实现,需要的可以参考一下
2022-11-13

Java数据结构中图的原理与实现是怎样的

小编今天带大家了解Java数据结构中图的原理与实现是怎样的,文中知识点介绍的非常详细。觉得有帮助的朋友可以跟着小编一起浏览文章的内容,希望能够帮助更多想解决这个问题的朋友找到问题的答案,下面跟着小编一起深入学习“Java数据结构中图的原理与
2023-06-29

Java数据结构之实现哈夫曼树的示例分析

这篇文章主要介绍了Java数据结构之实现哈夫曼树的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、与哈夫曼树相关的概念概念含义1. 路径从树中一个结点到另一个结点的
2023-06-15

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录