线段树(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 题目大意:有一个很上的面板, 往上面贴海报, 问最后最多有多少个海报没有被完全覆盖 解题思路:将贴海报倒着想, 对于每一张海报只 ...
随机推荐
- spring boot 2.0集成并使用redis
项目地址:https://gitee.com/indexman/spring_boot_in_action 前面一章介绍了spring boot自带的缓存,下面讲一下如何在2.0版本中集成并使用red ...
- win32改变静态控件的文本大小
HWND static_ = CreateWindow(L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_LEFT, 100, 100, 10 ...
- .net+bootstrap写的一个还不错的音乐网站
以前做的一款设计音乐网站,分享下. 技术用的是.net +sqlserver 大致的样子是这样的. 1.首页如下: 2.播放歌词页面如下:歌词自动滚动,且可悬停. 3.歌单信息页面如下: 详细页面如下 ...
- 【.Net Core】.Net Core 源码分析与深入理解 - 配置中心 Startup.cs (二)
源码版本: .Net Core 3.1.14 上篇文章: [.Net Core].Net Core 源码分析与深入理解 - 入口 Program.cs (一) 注意:本篇文章主要研究的是 Startu ...
- 被 AI 替代应该就在不远的将来
提问:golang 各种图片 转 webp 代码 一秒之后...... package main import ( "fmt" "image" "im ...
- python基础安装虚拟环境
1.pip install virtualenv或者pip3 install virtualenv 2.在要存放虚拟环境的地方创建一个venv文件夹,用来存放所有创建的虚拟环境,方便查找与管理 3.m ...
- 图查询语言 nGQL 简明教程 vol.01 快速入门
本文旨在让新手快速了解 nGQL,掌握方向,之后可以脚踩在地上借助文档写出任何心中的 NebulaGraph 图查询. 视频 本教程的视频版在B站这里. 准备工作 在正式开始 nGQL 实操之前,记得 ...
- MVVM框架模式
MVC框架模式 MVP框架模式 MVVM框架模式 MVVM模式即: 1.Model:数据层.网络数据操作,file文件操作,本地数据库操作: 2.View:视图层.布局加载,ui交互. 3.ViewM ...
- 聊聊微信小程序的流式(stream)响应请求
场景:类似ChatGPT的逐字显示效果. 流程:服务端我用Python的flask框架(向外提供API接口)实现,服务部署在replit上,Python调用azure 的chatgpt服务(需要申请) ...
- 我的闲鱼Python爬虫接单总结和经验,最高600元一单
最近,我在闲鱼上利用 Python 爬虫技术接了一些任务,想必你一定好奇,通过这样的方式,到底能不能挣钱,能挣多少钱?今天我就来分享一下我的经验和总结. 一.接单经历 之前 Vue 的作者尤大在微博上 ...