数据结构-线段树

article/2025/9/30 0:47:44

 如上图:

线段树是一颗满二叉树,叶子节点如果没有值,用null表示。 非空叶子节点就是基础数据,树中每个父亲节点代表左右孩子的结果集(比如求合,最大值,最小值等,自己定义算法,传入左右孩子即可)。

那么有n个元素,构建线段树需要开辟多少空间?

对于满二叉树,每一层节点数量都是前面所有节点数量之和+1。 也就是说,如果最后一层有n个节点,整棵树就约为2n个节点。

如果此时n为2的k次方,所有元素刚好在最后一层,整棵树约就是2n。   如果再多一个元素,就需要开辟下一层空间,如上图那样,整棵树就需要开辟4n的空间。

构建线段树:

使用两个数组来构建线段树,一个数组表示原始数据,另一个数组表示线段树。

如上图所示,如果传入一个数组,如何把它构建为线段树? 

public class SegmentTree<E> {//存储数据private E[] data;//用数组表示树结构private E[] tree;//定义算法类,计算左右孩子的结果。 此例中为求合private Merger<E> merger;//外界传入一个数组arr,将它构建为线段树(每个树节点就是左右孩子的和)public SegmentTree(E[] arr, Merger<E> merger) {this.merger = merger;data = (E[]) new Object[arr.length];for (int i = 0; i < arr.length; i++) {data[i] = arr[i];}tree = (E[]) new Object[4 * arr.length];buildSegmentTree(0, 0, arr.length - 1);}// 在treeIndex的位置创建表示区间[l...r]的线段树private void buildSegmentTree(int treeIndex, int l, int r) {if (l == r) {tree[l] = data[l];return;}int leftTreeIndex = leftChild(treeIndex);int rightTreeIndex = rightChild(treeIndex);int mid = (l + r) / 2;
//        int mid = l + (r - l) / 2;buildSegmentTree(leftTreeIndex, l, mid);buildSegmentTree(rightTreeIndex, mid + 1, r);tree[treeIndex] = merger.merge(tree[leftTreeIndex], tree[rightTreeIndex]);}// 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引private int leftChild(int index) {return 2 * index + 1;}// 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引private int rightChild(int index) {return 2 * index + 2;}public int getSize() {return data.length;}public E get(int index) {if (index < 0 || index >= data.length) {throw new IllegalArgumentException("Index is illegal.");}return data[index];}// 返回区间[queryL, queryR]的值public E query(int queryL, int queryR) {if (queryL < 0 || queryL >= data.length ||queryR < 0 || queryR >= data.length || queryL > queryR) {throw new IllegalArgumentException("Index is illegal.");}return query(0, 0, data.length - 1, queryL, queryR);}// 在以treeIndex为根的线段树中[l...r]的范围里,搜索区间[queryL...queryR]的值private E query(int treeIndex, int l, int r, int queryL, int queryR) {if (l == queryL && r == queryR) {return tree[treeIndex];}int leftTreeIndex = leftChild(treeIndex);int rightTreeIndex = rightChild(treeIndex);int mid = (l + r) / 2;if (queryL >= mid + 1) {return query(rightTreeIndex, mid + 1, r, queryL, queryR);} else if (queryR <= mid) {return query(leftTreeIndex, l, mid, queryL, queryR);}E leftResult = query(leftTreeIndex, l, mid, queryL, mid);E rightResult = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);return merger.merge(leftResult, rightResult);}// 将index位置的值,更新为epublic void set(int index, E e) {if (index < 0 || index >= data.length) {throw new IllegalArgumentException("Index is illegal");}//直接把数据数组中对应的值改掉data[index] = e;//重构线段树set(0, 0, data.length - 1, index, e);}// 在以treeIndex为根的线段树中更新index的值为eprivate void set(int treeIndex, int l, int r, int index, E e) {//说明找到树中最底层那个节点了if (l == r) {tree[treeIndex] = e;return;}int mid = (l + r) / 2;int leftTreeIndex = leftChild(treeIndex);int rightTreeIndex = rightChild(treeIndex);if (index > mid) {set(rightTreeIndex, mid + 1, r, index, e);} else {set(leftTreeIndex, l, mid, index, e);}tree[treeIndex] = merger.merge(tree[leftTreeIndex], tree[rightTreeIndex]);}
}
public interface Merger<E> {/*** 计算两个节点的结果集* @param a* @param b* @return*/E merge(E a, E b);
}

做一道题:

/*** 给你一个数组 nums ,请你完成两类查询,其中一类查询要求更新数组下标对应的值,另一类查询要求返回数组中某个范围内元素的总和。* <p>* 实现 NumArray 类:* <p>* NumArray(int[] nums) 用整数数组 nums 初始化对象 void update(int index, int val) 将 nums[index] 的值更新为 val int sumRange(int left,* int right) 返回子数组 nums[left, right] 的总和(即,nums[left] + nums[left + 1], ..., nums[right])* <p>* 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/range-sum-query-mutable 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。*/
public class NumArray {private SegmentTree<Integer> tree;public NumArray(int[] nums) {if (nums.length != 0) {Integer[] data = new Integer[nums.length];for (int i = 0; i < nums.length; i++) {data[i] = nums[i];}tree = new SegmentTree<Integer>(data, (a, b) -> a + b);}}public void update(int i, int val) {tree.set(i,val);}public int sumRange(int i, int j) {return tree.query(i,j);}
}

 


http://chatgpt.dhexx.cn/article/CQJNBJdd.shtml

相关文章

线段树模板

线段树是我接触到的第一个高级数据结构&#xff0c;也是我接触过的模板最长的一种代码&#xff0c;但他的实用性真的很强&#xff0c;绝大部分的区间问题都可以用线段树解决点&#xff0c;所以今天我打算简单介绍一下我对线段树的理解 先放一张百度来的线段树图片&#xff1a;…

C++ 线段树

目录&#xff1a; 线段树基本常识 线段树的的存储 线段树的基本操作 1. 建树 2. 单点查询 3. 单点修改 4. 区间查询 5. 区间修改 例题推荐&#xff1a; 线段树 1 线段树 2 线段树基本常识 线段树是一种二叉搜索树&#xff08;即每个节点最多有两颗子树&#xff09;&…

线段树(java)

线段树描述&#xff1a; 线段树是一种二叉搜索树&#xff0c;与区间树相似&#xff0c;它将一个区间划分成一些单元区间&#xff0c;每个单元区间对应线段树中的一个叶结点。 使用线段树可以快速的查找某一个节点在若干条线段中出现的次数&#xff0c;时间复杂度为O(logN)。而未…

什么是线段树

线段树的概念 线段树是一种二叉搜索树&#xff0c;与区间树相似&#xff0c;它将一个区间划分成一些单元区间&#xff0c;每个单元区间对应线段树中的一个叶结点。 对于线段树中的每一个非叶子节点[a,b]&#xff0c;它的左儿子表示的区间为[a,(ab)/2]&#xff0c;右儿…

李超线段树

什么是李超线段树 先以一个问题引入&#xff1a; 在平面上有两种操作&#xff08;强制在线&#xff09;&#xff1a; 插入一条表达式为 L : y k*xb 的直线&#xff0c;给出 k ,b 。 给出 t&#xff0c;求当前所有直线中与直线 x t 交点的纵坐标最大是多少 直线取 max 应该是…

线段树详解 (原理,实现与应用)

线段树详解 By 岩之痕 目录&#xff1a; 一&#xff1a;综述 二&#xff1a;原理 三&#xff1a;递归实现 四&#xff1a;非递归原理 五&#xff1a;非递归实现 六&#xff1a;线段树解题模型 七&#xff1a;扫描线 八&#xff1a;可持久化 (主席树) 九&#xff1a;练习题 一&a…

线段树详解(附图解,模板及经典例题分析)

1.基本概念 ​ 线段树是一种常用来维护 区间信息 \color{Maroon}区间信息 区间信息 的数据结构 ​ 可以在 O(logn) 的时间复杂度内实现单点修改、区间修改、区间查询&#xff08;区间求和&#xff0c;求区间的最大值&#xff0c;求区间的最小值&#xff09;等操作 ​ 如下图…

线段树详解

文章目录 1. 什么是线段树2. 线段树的用处3. 线段树的模板题4. 动态开点线段树5. 权值线段树5. 可持久化线段树 1. 什么是线段树 顾名思义 &#xff0c; 线段树是一棵二叉树 &#xff0c; 但不同的是这棵树的结点储存的值是一个数列中 [ l , r ] [l,r] [l,r] 的某个需要的值 …

超详解线段树(浅显易懂,几乎涵盖所有线段树类型讲解,匠心之作,图文并茂)

一&#xff0c;什么是线段树&#xff1f; 线段树是怎样的树形结构? 线段树是一种二叉搜索树&#xff0c;而二叉搜索树&#xff0c;首先满足二叉树&#xff0c;即每个结点最多有两颗子树&#xff0c;并且是一颗搜索树&#xff0c;我们要知道&#xff0c;线段树的每个结点都存储…

线段树 从入门到进阶(超清晰,简单易懂)

目录 第一部 概念引入第二部 简单&#xff08;无pushdown&#xff09;的线段树1、单点修改&#xff0c;区间查询2、区间修改&#xff0c;单点查询 第三部 进阶线段树第四部 乘法&#xff08;根号&#xff09;线段树1、乘法线段树2、根号线段树模板题与代码&#xff1a;单点修改…

Navicat Premium 数据库开发工具

官网地址&#xff1a;https://www.navicat.com.cn/ Navicat Premium 是一套数据库开发工具&#xff0c;让你从单一应用程序中同时连接 MySQL、MariaDB、MongoDB、SQL Server、Oracle、PostgreSQL 和 SQLite 数据库。它与 Amazon RDS、Amazon Aurora、Amazon Redshift、Microso…

Navicat 一款数据库开发软件

Navicat premium是一款数据库管理工具&#xff0c;非常好用&#xff0c;能复制表结构等&#xff0c;大大简化了后端开发的工程量&#xff0c;其软件示意图如下 Navicat Premium 15 已经发布了&#xff0c;但是价格昂贵&#xff0c;所以将 no-money version 的双手奉上。 使用…

Java学习中的数据库和数据库开发工具

一、数据库 1、数据库&#xff0c;通常是一个戒一组文件&#xff0c;保存了一些符合特定规格的数据&#xff0c;数据库对应的英询单词是DataBase&#xff0c;简称DB&#xff1b;数据库软件称为数据库管理系统&#xff0c;英文简称DBMS&#xff0c;全称为DataBase Management S…

五大主流数据库深度对比!数据库开发、管理看这篇就够了

开发数据库应用&#xff0c;选择一个好的数据库是非常重要的。目前&#xff0c;商品化的数据库管理系统以关系型数据库为主导产品&#xff0c; 技术比较成熟。面向对象的数据库管理系统技术先进&#xff0c;数据库易于开发、维护。无论是关系型数据库还是非关系型数据库&#x…

mysql数据库开发环境_MySQL数据库教程-环境与集成开发工具

MySQL数据库基础教程 数据库是存储数据与管理数据的基础,也是目前绝大多数动态网站开发所必备的基础知识。MySQL数据库教程系列文章主要以MySQL数据库管理系统为例对关系型数据库相关定义与操作等进行讲解。并结合php与PDO等讲解,为初学者提供快速学习PHP动态网站开发所需的知…

C/C++ MySQL数据库开发

C/C++ MySQL数据库开发笔记 MySQL启动方式手动启动MySQL数据库:命令行方式启动MySQL数据库:配置环境变量MySQL在Linux下安装MySQL的配置文件重要参数设置C/C++ 访问MySQL数据库(MySQL开发环境的配置(MySQL开发头文件和库文件)MySQL 数据库的连接MySQL 数据类型以及对应的 …

1、 数据库开发规范

第1章 数据库开发规范的制定 数据库设计步骤&#xff1a; 1、数据结构设计&#xff1a;逻辑设计-》物理设计 2、实际工作中&#xff1a;逻辑设计物理设计 3、物理设计&#xff1a;表名字段名字段类型 数据库设计几个规范&#xff1a; 数据库命名规范、数据库基本设计规范…

数据库开发-2-开发数据库的要点

Lec2-开发数据库的要点 1. 开发成功数据库应用的特点 需要理解数据库体系结构需要理解锁和并发控制特性&#xff1a;每个数据库都以不同的方式实现最重要的是不要把数据库当"黑盒" 要了解每一个数据库的体系结构和特征最常见的问题就是因为我们对数据库本身了解不足…

数据库开发(Sqlite)

1、数据库开发 1.1 数据与数据管理 什么是信息&#xff1f;   信息是指对现实世界存在方式或运动状态的反应。 什么是数据&#xff1f;   数据是指存储在某一媒体上&#xff0c;能够被识别的物理符号&#xff1b;   数据的概念在数据处理领域已经被大为拓宽&#xff0c…

《软件测试的艺术》第4章:测试用例的设计-白盒测试

写在前面&#xff1a;原书中包含白盒测试、黑盒测试、错误猜测、测试策略四个小节&#xff0c;涵盖内容较多&#xff0c;因此按章节拆分叙述。 《软件测试的艺术》&#xff1a; 白盒测试--语句覆盖 语句覆盖的用例设计原则&#xff1a;将程序中的每条语句至少执行一次。 白盒…