线段树(SegmentTree)

- 对于数组应用于区间染色实现为On,而线段树是O(logn)

- 什么是线段树:对于一个二叉树,每一个节点存储的是一个线段或是一个区间相应的信息。





查询

更新
#pragma once
#include <cassert>
#include <functional>
template<typename T>
class SegmentTree {
public:
SegmentTree() noexcept = default;
explicit SegmentTree(const T *const arr, const int n, std::function<T(T, T)> func) : data(new T[n]),
tree(new T[4 * n]),
size(n),
function(func) {
for (int i = 0; i < n; ++i) {
data[i] = arr[i];
}
//构建线段树 根索引为0,左边界为0,有边界为 size-1
buildSegmentTree(0, 0, size - 1);
}
~SegmentTree() noexcept {
delete[] data;
data = nullptr;
delete[] tree;
tree = nullptr;
}
constexpr int getSize() const noexcept {
return size;
}
T get(const int index) const {
assert(index >= 0 && index < size);
return data[index];
}
T query(const int queryL, const int queryR) {
assert(queryL >= 0 && queryL < size && queryR >= 0 && queryR < size && queryL <= queryR);
return query(0, 0, size - 1, queryL, queryR);
}
void set(const int index, const T &e) {
assert(index >= 0 && index < size);
data[index] = e;
set(0, 0, size - 1, index, e);
}
void print() const {
std::cout << "[";
for (int i = 0; i < size * 4; ++i) {
if (tree[i] != NULL) {
std::cout << tree[i];
} else {
std::cout << "0";
}
if (i != size * 4 - 1) {
std::cout << ", ";
}
}
std::cout << "]" << std::endl;
}
private:
void set(const int treeIndex, const int l, const int r, const int index, const T &e) {
//都叶子了,一定是它了,更新它
if (l == r) {
tree[treeIndex] = e;
return;
}
int mid = l + (r - l) / 2;
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
//要找的索引大于中间值,一定在右边
if (index >= mid + 1) {
set(rightTreeIndex, mid + 1, r, index, e);
} else if (index <= mid) { //否则在左边
set(leftTreeIndex, l, mid, index, e);
}
//更新...
tree[treeIndex] = function(tree[leftTreeIndex], tree[rightTreeIndex]);
}
//在以treeIndex为根的线段树[l...r]的范围里,搜索区间[queryL,queryR]的值
int query(const int treeIndex, const int l, const int r, const int queryL, const int queryR) {
//如果左右相同就找到了
if (l == queryL && r == queryR) {
return tree[treeIndex];
}
int mid = l + (r - l) / 2;
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
//如果查找的范围左边界大于中间
if (mid + 1 <= queryL) {
//那么就不用查找左边
return query(rightTreeIndex, mid + 1, r, queryL, queryR);
//如果查找的范围右边小于中间
} else if (mid >= queryR) {
//那么就不用查找右边
return query(leftTreeIndex, l, mid, queryL, queryR);
}
//如果查找的范围占用两个区间
T leftResult = query(leftTreeIndex, l, mid, queryL, mid);
T rightResult = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);
return function(leftResult, rightResult);
}
void buildSegmentTree(const int treeIndex, const int left, const int right) {
//如果左右相等就说明递归到底
if (left == right) {
tree[treeIndex] = data[left];
return;
}
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
int mid = left + (right - left) / 2;
//递归左右孩子根为左右孩子索引,左右边界以中间为界
buildSegmentTree(leftTreeIndex, left, mid);
buildSegmentTree(rightTreeIndex, mid + 1, right);
//线段存储信息根据业务写相应的代码,以求和为例,
tree[treeIndex] = function(tree[leftTreeIndex], tree[rightTreeIndex]);
}
constexpr int leftChild(const int index) const noexcept {
return index * 2 + 1;
}
constexpr int rightChild(const int index) const noexcept {
return index * 2 + 2;
}
private:
std::function<T(T, T)> function;
T *tree;
T *data;
int size;
};
#include <iostream>
#include "SegmentTree.h"
int main() {
int nums[] = {-2, 0, 3, -5, 2, -1};
SegmentTree<int> *segmentTree = new SegmentTree<int>(nums, sizeof(nums) / sizeof(int), [](int a, int b) -> int {
return a + b;
});
std::cout << segmentTree->query(0,2) << std::endl;
std::cout << segmentTree->query(2,5) << std::endl;
std::cout << segmentTree->query(0,5) << std::endl;
segmentTree->print();
segmentTree->set(0,0);
segmentTree->print();
std::cout << segmentTree->query(0,2) << std::endl;
std::cout << segmentTree->query(2,5) << std::endl;
std::cout << segmentTree->query(0,5) << std::endl;
return 0;
}
输出
1
-1
-3
[-3, 1, -4, -2, 3, -3, -1, -2, 0, 0, 0, -5, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[-1, 3, -4, 0, 3, -3, -1, 0, 0, 0, 0, -5, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
3
-1
-1
LeetCode
给你一个数组 nums ,请你完成两类查询。
- 其中一类查询要求 更新 数组 nums 下标对应的值
- 另一类查询要求返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 ,其中 left <= right
实现 NumArray 类:
- NumArray(int[] nums) 用整数数组 nums 初始化对象
- void update(int index, int val) 将 nums[index] 的值 更新 为 val
- int sumRange(int left, int right) 返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和 (即,nums[left] + nums[left + 1], ..., nums[right])
class NumArray
{
public:
NumArray(vector<int> nums)
{
if (nums.size() > 0)
{
int *data = new int[nums.size()];
for (int i = 0; i < nums.size(); ++i)
{
data[i] = nums[i];
}
segmentTree = new SegmentTree<int>(data, nums.size(), [](int a, int b) -> int
{ return a + b; });
}
}
void update(int i, int val)
{
assert(segmentTree != nullptr);
segmentTree->set(i, val);
}
int sumRange(int i, int j)
{
assert(segmentTree != nullptr);
return segmentTree->query(i, j);
}
private:
template<typename T>
class SegmentTree {
public:
SegmentTree() noexcept = default;
explicit SegmentTree(const T *const arr, const int n, std::function<T(T, T)> func) : data(new T[n]),
tree(new T[4 * n]),
size(n),
function(func) {
for (int i = 0; i < n; ++i) {
data[i] = arr[i];
}
//构建线段树 根索引为0,左边界为0,有边界为 size-1
buildSegmentTree(0, 0, size - 1);
}
~SegmentTree() noexcept {
delete[] data;
data = nullptr;
delete[] tree;
tree = nullptr;
}
constexpr int getSize() const noexcept {
return size;
}
T get(const int index) const {
assert(index >= 0 && index < size);
return data[index];
}
T query(const int queryL, const int queryR) {
assert(queryL >= 0 && queryL < size && queryR >= 0 && queryR < size && queryL <= queryR);
return query(0, 0, size - 1, queryL, queryR);
}
void set(const int index, const T &e) {
assert(index >= 0 && index < size);
data[index] = e;
set(0, 0, size - 1, index, e);
}
void print() const {
std::cout << "[";
for (int i = 0; i < size * 4; ++i) {
if (tree[i] != NULL) {
std::cout << tree[i];
} else {
std::cout << "0";
}
if (i != size * 4 - 1) {
std::cout << ", ";
}
}
std::cout << "]" << std::endl;
}
private:
void set(const int treeIndex, const int l, const int r, const int index, const T &e) {
//都叶子了,一定是它了,更新它
if (l == r) {
tree[treeIndex] = e;
return;
}
int mid = l + (r - l) / 2;
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
//要找的索引大于中间值,一定在右边
if (index >= mid + 1) {
set(rightTreeIndex, mid + 1, r, index, e);
} else if (index <= mid) { //否则在左边
set(leftTreeIndex, l, mid, index, e);
}
//更新...
tree[treeIndex] = function(tree[leftTreeIndex], tree[rightTreeIndex]);
}
//在以treeIndex为根的线段树[l...r]的范围里,搜索区间[queryL,queryR]的值
int query(const int treeIndex, const int l, const int r, const int queryL, const int queryR) {
//如果左右相同就找到了
if (l == queryL && r == queryR) {
return tree[treeIndex];
}
int mid = l + (r - l) / 2;
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
//如果查找的范围左边界大于中间
if (mid + 1 <= queryL) {
//那么就不用查找左边
return query(rightTreeIndex, mid + 1, r, queryL, queryR);
//如果查找的范围右边小于中间
} else if (mid >= queryR) {
//那么就不用查找右边
return query(leftTreeIndex, l, mid, queryL, queryR);
}
//如果查找的范围占用两个区间
T leftResult = query(leftTreeIndex, l, mid, queryL, mid);
T rightResult = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);
return function(leftResult, rightResult);
}
void buildSegmentTree(const int treeIndex, const int left, const int right) {
//如果左右相等就说明递归到底
if (left == right) {
tree[treeIndex] = data[left];
return;
}
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
int mid = left + (right - left) / 2;
//递归左右孩子根为左右孩子索引,左右边界以中间为界
buildSegmentTree(leftTreeIndex, left, mid);
buildSegmentTree(rightTreeIndex, mid + 1, right);
//线段存储信息根据业务写相应的代码,以求和为例,
tree[treeIndex] = function(tree[leftTreeIndex], tree[rightTreeIndex]);
}
constexpr int leftChild(const int index) const noexcept {
return index * 2 + 1;
}
constexpr int rightChild(const int index) const noexcept {
return index * 2 + 2;
}
private:
std::function<T(T, T)> function;
T *tree;
T *data;
int size;
};
SegmentTree<int> *segmentTree;
};
线段树(SegmentTree)的更多相关文章
- java——线段树 SegmentTree
应用: 区间染色 区间查询 线段树不是完全二叉树,线段树是平衡二叉树 使用数组来实现线段树:存储空间为4n 以下是使用数组实现的静态线段树: public class SegmentTree<E ...
- 模板 - 数据结构 - 线段树/SegmentTree
区间求加法和: 单点修改的,普通线段树. struct SegmentTree { #define ls (o<<1) #define rs (o<<1|1) static c ...
- 【LeetCode】线段树 segment-tree(共9题)+ 树状数组 binary-indexed-tree(共5题)
第一部分---线段树:https://leetcode.com/tag/segment-tree/ [218]The Skyline Problem [307]Range Sum Query - Mu ...
- 线段树(SegmentTree)基础模板
线段树模板题来源:https://www.lintcode.com/problem/segment-tree-build/description 201. 线段树的构造 /** * Definitio ...
- 【hihoCoder】第20周 线段树
题目: 输入 每个测试点(输入文件)有且仅有一组测试数据. 每组测试数据的第1行为一个整数N,意义如前文所述. 每组测试数据的第2行为N个整数,分别描述每种商品的重量,其中第i个整数表示标号为i的商品 ...
- 【BZOJ-2325】道馆之战 树链剖分 + 线段树
2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 1153 Solved: 421[Submit][Statu ...
- 【Codeforces720D】Slalom 线段树 + 扫描线 (优化DP)
D. Slalom time limit per test:2 seconds memory limit per test:256 megabytes input:standard input out ...
- 【Codeforces718C】Sasha and Array 线段树 + 矩阵乘法
C. Sasha and Array time limit per test:5 seconds memory limit per test:256 megabytes input:standard ...
- 【Codeforces717F】Heroes of Making Magic III 线段树 + 找规律
F. Heroes of Making Magic III time limit per test:3 seconds memory limit per test:256 megabytes inpu ...
- POJ 2528 Mayor's posters (线段树)
题目链接:http://poj.org/problem?id=2528 题目大意:有一个很上的面板, 往上面贴海报, 问最后最多有多少个海报没有被完全覆盖 解题思路:将贴海报倒着想, 对于每一张海报只 ...
随机推荐
- golang常用库:gorilla/mux-http路由库使用
golang常用库:gorilla/mux-http路由库使用 golang常用库:配置文件解析库/管理工具-viper使用 golang常用库:操作数据库的orm框架-gorm基本使用 一:gola ...
- 树莓派开发笔记(十六):树莓派4B+安装mariadb数据库(mysql开源分支)并测试基本操作
前言 树莓派使用数据库时,优先选择sqlite数据库,但是sqlite是文件数据库同时仅针对于单用户的情况,考虑到多用户的情况,在树莓派上部署安装mariadb数据库服务(mysql的开源分支), ...
- gorm入门学习
文档 https://learnku.com/docs/gorm/v2/v2_release_note/9756 https://gorm.cn/zh_CN/docs/ 下载安装 go get -u ...
- Ubuntu虚拟机开机显示initramfs
因为我的虚拟机路径放在了移动硬盘当中,所以连接有点失常就断开了,紧接着虚拟机也异常关闭了. 重启后进入了initramfs界面 查看出错的分区,如下图所示,是/dev/sda5分区有损坏 解决方法: ...
- matrox的RAP4G4C12 CXP采集卡软件安装
Hello-FPGA info@hello-fpga.cOM matrox的RAP4G4C12 CXP采集卡软件安装 目录 matrox的RAP4G4C12 CXP采集卡软件安装 4 1 前言 4 2 ...
- maven配置全局私服地址和阿里云仓库
直接上配置代码 <?xml version="1.0" encoding="UTF-8"?> <!-- Licensed to the Apa ...
- 【Azure 应用服务】在App Service中新建WebJob时候遇见错误,不能成功创建新的工作任务
问题描述 在Azure App Service界面上,添加新的Web Job(工作任务)时,一直添加失败.无详细错误提示,在App Service的Activity Logs(活动日志)中,根本没有添 ...
- 机器学习可解释性--shapvalue
A Unified Approach to Interpreting Model Predictions trusting a prediction or trusting a model 如果⼀个机 ...
- Java 类方法的声明和使用
1 /** 2 * 3 * @Description 4 * @author Bytezero·zhenglei! Email:420498246@qq.com 5 * @version 6 * @d ...
- 计算机网络-IP地址
目录 子网划分 定长子网划分 子网划分的方法 子网掩码 可变长子网划分 无类别编址 网络前缀 路由聚合 特殊用途的IP地址 专用网络地址 链路本地地址 运营商级NAT共享地址 用于文档的测试网络地址 ...