• 对于数组应用于区间染色实现为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. Redhat6更改yum源

    最近虚拟机中安装了redhat6.3企业版,自带的yum用不起来,软件都找不到. 网上搜了一下说是没付钱...,需要改下yum源.操作步骤如下: 1.切换到yum源存放目录 [root@rhel6 ~ ...

  2. win32-Transparent的使用

    这个api的功能主要是实现"透明" 原理: Transparent将hdc中bmp的特定颜色"透明化" #include <Windows.h> # ...

  3. 2021-10-11 vue的第三方组件二次封装

    原理 v-bind="$attrs"继承所有属性和props. v-on="$listeners"继承所有的方法. <template> <d ...

  4. RK3568开发笔记(三):RK3568虚拟机基础环境搭建之更新源、安装网络工具、串口调试、网络连接、文件传输、安装vscode和samba共享服务

    前言   开始搭建RK3568的基础虚拟机,具备基本的通用功能,主要包含了串口工具minicom,远程登陆ssh,远程传输filezilla,代码编辑工具vscode.   虚拟机   文档对对虚拟机 ...

  5. Java JVM——3.运行时数据区概述及线程

    运行时数据区概述 在JVM 中的位置 内部划分 当我们通过前面的:类的加载 → 验证 → 准备 → 解析 → 初始化 这几个阶段完成后,执行引擎就会对我们的类进行使用,同时执行引擎将会使用到我们的运行 ...

  6. collection.abc模块下的抽象基类UML类图说明

    说明 Iterable.Container和Sized 每个容器都应该继承这三个抽象基类,或者实现兼容的协议.Iterable通过__iter__方法支持迭代, Container通过__contai ...

  7. PHP项目&TP框架&SQL&XSS&架构&路由&调试&写法

    开发基础-TP框架-入口&调试&路由&写法等 参考手册-TP5开发手册-为了掌握了解框架 首页文件看APP_PATH定义-为了后期分析核心代码 全局搜索:THINK_VERSI ...

  8. Java 线程通信 例子:使用俩个线程打印1-100.线程1 线程2 交替打印

    1 package bytezero.threadcommunication; 2 3 /** 4 * 线程通信的例子:使用俩个线程打印1-100.线程1 线程2 交替打印 5 * 6 * 涉及到的三 ...

  9. sentinel的见解

    Sentinel 是面向分布式.多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量控制.熔断降级.热点流量防护等多个维度来帮助开发者保障微服务的稳定性.   在 Sentinel 里面,所 ...

  10. [VueJsDev] 其他知识 - 单词本

    [VueJsDev] 目录列表 https://www.cnblogs.com/pengchenggang/p/17037320.html 单词本z 这里的单词就是很随性的记忆,来源有生活中能见到的, ...