• 对于数组应用于区间染色实现为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

307. 区域和检索 - 数组可修改

给你一个数组 nums ,请你完成两类查询。

  1. 其中一类查询要求 更新 数组 nums 下标对应的值
  2. 另一类查询要求返回数组 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)的更多相关文章

  1. java——线段树 SegmentTree

    应用: 区间染色 区间查询 线段树不是完全二叉树,线段树是平衡二叉树 使用数组来实现线段树:存储空间为4n 以下是使用数组实现的静态线段树: public class SegmentTree<E ...

  2. 模板 - 数据结构 - 线段树/SegmentTree

    区间求加法和: 单点修改的,普通线段树. struct SegmentTree { #define ls (o<<1) #define rs (o<<1|1) static c ...

  3. 【LeetCode】线段树 segment-tree(共9题)+ 树状数组 binary-indexed-tree(共5题)

    第一部分---线段树:https://leetcode.com/tag/segment-tree/ [218]The Skyline Problem [307]Range Sum Query - Mu ...

  4. 线段树(SegmentTree)基础模板

    线段树模板题来源:https://www.lintcode.com/problem/segment-tree-build/description 201. 线段树的构造 /** * Definitio ...

  5. 【hihoCoder】第20周 线段树

    题目: 输入 每个测试点(输入文件)有且仅有一组测试数据. 每组测试数据的第1行为一个整数N,意义如前文所述. 每组测试数据的第2行为N个整数,分别描述每种商品的重量,其中第i个整数表示标号为i的商品 ...

  6. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  7. 【Codeforces720D】Slalom 线段树 + 扫描线 (优化DP)

    D. Slalom time limit per test:2 seconds memory limit per test:256 megabytes input:standard input out ...

  8. 【Codeforces718C】Sasha and Array 线段树 + 矩阵乘法

    C. Sasha and Array time limit per test:5 seconds memory limit per test:256 megabytes input:standard ...

  9. 【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 ...

  10. POJ 2528 Mayor's posters (线段树)

    题目链接:http://poj.org/problem?id=2528 题目大意:有一个很上的面板, 往上面贴海报, 问最后最多有多少个海报没有被完全覆盖 解题思路:将贴海报倒着想, 对于每一张海报只 ...

随机推荐

  1. Go微服务框架go-kratos实战学习06:配置中心使用-nacos作为配置中心和 file作为配置存储

    一.kratos 配置介绍 配置文件的作用就是把一些会变化的配置项单独存放,与程序相剥离. 把配置项进行单独管理. kratos 支持多种形式的配置, 比如 file,环境变量. 还支持一些配置软件, ...

  2. 看看 ChatGPT 给的前端面试题

    以下是一些可能出现在中国互联网公司前端开发工程师面试中的题目: 解释一下 CSS 盒模型,并说明其中的各个部分. 请解释一下响应式设计是什么,以及你是如何实现响应式设计的. 什么是跨域资源共享(COR ...

  3. 合并区间(区间排序,vector的动态扩容的应用)

    以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] .请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入 ...

  4. 【LeetCode哈希表#3】快乐数(set)

    快乐数 力扣题目链接(opens new window) 编写一个算法来判断一个数 n 是不是快乐数. 「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程 ...

  5. 记一次酣畅淋漓的 K8s Ingress 排错过程(302,404,503,...)

    故事开始 第 1 关:[流量重定向到 /] 第 2 关:[应用返回 302,重定向到 /,引入 503 错误] 第 3 关:[静态资源访问遇到 503 问题] 第 4 关:[静态资源访问遇到 403 ...

  6. 服务网关ZUUL过滤器

    过滤器两个功能: 1.其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础: 2.过滤器功能则负责对请求的处理过程进行预干预,是实现请求校验.服务聚合等功能的基础. 有4类 ...

  7. TensorFlow 回归模型

    TensorFlow 回归模型 首先,导入所需的库和模块.代码中使用了numpy进行数值计算,matplotlib进行数据可视化,tensorflow进行机器学习模型的构建和训练,sklearn进行多 ...

  8. ASP.NET Core 选项

    目录 1,选项接口 2,注入配置与IOptions 3,IOptionsSnapshot 首先要了解 ASP.NET Core 中的配置,请点击这里了解:https://www.cnblogs.com ...

  9. 关闭mysql上锁的表/数据

    一.输入查询语句,查看是否有数据被上锁 select * from information_schema.innodb_trx; 取 trx_mysql_thread_id 字段值 kill < ...

  10. 开源短信项目 platform-sms 发布了新版本 0.5.0

    Github : https://github.com/makemyownlife/platform-sms 短信服务 platform-sms 0.5.0 发布 ,新的版本做了非常多的优化和改进. ...