教你搞懂线段树,从基础到提高

news2024/12/28 17:46:28

秋名山码民的主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
🙏作者水平有限,如发现错误,还请私信或者评论区留言!

目录

  • 前言
  • 线段树逻辑概念
    • 线段树的俩个重要用处
  • 代码实现线段树
  • 题目巩固
  • 最后


前言

线段树算是比较难的一个数据结构,当时我高中提高组就没学懂,细数我学线段树也学了4遍,最早学的时候一脸懵逼,最近在刷题中发现其在蓝桥杯中也有考察,就寻思写一篇博客来巩固。

什么是线段树,线段树有什么用,线段树怎么写,能不能背过???

我认为对于打比赛的各位来说,线段树和前缀和一样,不能算做算法,它更多的是一种工具,一种时间复杂度为O(logn)的单点修改,区间查询的工具

线段树逻辑概念

给定一个1~7的区间我们来维护它,将其转换为一个二叉树(线段树本身就是一个二叉树

  1. 最上面的根的权值,为28,1~7的和
  2. 二叉树开分,左边为1~4的和为10,右边为5 ~ 6的和为21
  3. 1~4在开分,左边为3,右边为7 ,5 ~6开分,左边为14,右边为7
  4. 同上,直到不能再分

L,R:
在这里插入图片描述

class node{
    int l,r;
    int sum;
}

在这里插入图片描述

线段树的俩个重要用处

1. 单点修改

如上图我们将5变成8,其中要修改的为1~ 7,5~ 7,5~ 6,5,

2. 区间查询

如上图我们计算2 ~ 5区间,如果完全包含某个区间,则退出,否则进行递归,用黄圈表示需要递归的区间

  1. 1~7区间,递归左边,1 ~4区间,发现还没有被完全包含,进行左右俩边都递归,1 ~2,3 ~4,此刻,3 ~4完全包含,不进行递归,继续递归1~ 2,2被完全包含
  2. 5~ 7区间,同上,进行递归
  3. 进行回溯区间,只算完全包含的区间,2+7+8 = 17

总结:
如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
如果这个区间的左儿子和目标区间有交集,那么搜索左儿子
如果这个区间的右儿子和目标区间有交集,那么搜索右儿子

时间复杂度均为O(logn)

代码实现线段树

上面我们说线段树的俩个重要的用法,思考一下大概需要几个函数能实现?

  1. pushup:用子节点信息更新当前节点信息
  2. build:在一段区间上初始化线段树
  3. modify:修改
  4. query:查询

动态求连续区间和
在这里插入图片描述

import java.io.*;

/**
 * @Author 秋名山码神
 * @Date 2023/2/9
 * @Description
 */
public class 动态求连续区间和 {
    static int n, k;
    static int[] a = new int[100100];
    static Node[] tr = new Node[400400];
    static class Node{
        int l, r, sum;
        Node(int l, int r, int sum) {
            this.l = l;
            this.r = r;
            this.sum = sum;
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        String[] cd = br.readLine().split(" ");
        n = Integer.parseInt(cd[0]);
        k = Integer.parseInt(cd[1]);

        String[] line = br.readLine().split(" ");
        for (int i=1;i<=n;i++)
            a[i] = Integer.parseInt(line[i-1]);

        build(1, 1, n);

        while (k-- > 0) {
            String[] li = br.readLine().split(" ");
            int k = Integer.parseInt(li[0]), l = Integer.parseInt(li[1]), r = Integer.parseInt(li[2]);
            if(k == 0) {
                bw.write(String.valueOf(query(1, l, r)));
                bw.write("\n");
            } else
                modify(1, l, r);
        }
        bw.flush();
    }
    static void pushUp(int u) {
        tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
    }

    static void build(int u, int l, int r) {
        if(l == r) tr[u] = new Node(l , r, a[l]);
        else {
            tr[u] = new Node(l ,r, 0);
            int mid = l + r >> 1;
            build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
            pushUp(u);
        }
    }

    static int query(int u, int l, int r) {
        if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
        int mid = tr[u].l + tr[u].r >> 1, sum = 0;
        if(l <= mid) sum += query(u << 1, l, r);
        if(r > mid) sum += query(u << 1 | 1, l , r);
        return sum;
    }

    static void modify(int u, int x, int v) {
        if(tr[u].l == tr[u].r) tr[u].sum += v;
        else {
            int mid = tr[u].l + tr[u].r >> 1;
            if(x <= mid) modify(u << 1, x , v);
            else modify(u << 1 | 1, x, v);
            pushUp(u);
        }
    }
}


题目巩固

区间和的个数

在这里插入图片描述

class Solution {
    public int countRangeSum(int[] nums, int lower, int upper) {
        int count = 0;
        int length = nums.length;
        long[] sums = new long[length + 1];
        for (int i = 0; i < length; i++) {
            sums[i + 1] = sums[i] + nums[i];
        }
        Set<Long> set = new HashSet<Long>();
        for (int i = 0; i <= length; i++) {
            long sum = sums[i];
            set.add(sum);
            set.add(sum - upper);
            set.add(sum - lower);
        }
        List<Long> sumsList = new ArrayList<Long>(set);
        Collections.sort(sumsList);
        Map<Long, Integer> ranks = new HashMap<Long, Integer>();
        int size = sumsList.size();
        for (int i = 0; i < size; i++) {
            ranks.put(sumsList.get(i), i);
        }
        SegmentTree st = new SegmentTree(size);
        for (int i = 0; i <= length; i++) {
            long sum = sums[i];
            int rank = ranks.get(sum);
            long minSum = sum - upper, maxSum = sum - lower;
            int start = ranks.get(minSum), end = ranks.get(maxSum);
            count += st.getCount(start, end);
            st.add(rank);
        }
        return count;
    }
}

class SegmentTree {
    int length;
    int[] tree;

    public SegmentTree(int length) {
        this.length = length;
        this.tree = new int[length * 4];
    }

    public int getCount(int start, int end) {
        return getCount(start, end, 0, 0, length - 1);
    }

    public void add(int rank) {
        add(rank, 0, 0, length - 1);
    }

    private int getCount(int rangeStart, int rangeEnd, int index, int treeStart, int treeEnd) {
        if (rangeStart > rangeEnd) {
            return 0;
        }
        if (rangeStart == treeStart && rangeEnd == treeEnd) {
            return tree[index];
        }
        int mid = treeStart + (treeEnd - treeStart) / 2;
        if (rangeEnd <= mid) {
            return getCount(rangeStart, rangeEnd, index * 2 + 1, treeStart, mid);
        } else if (rangeStart > mid) {
            return getCount(rangeStart, rangeEnd, index * 2 + 2, mid + 1, treeEnd);
        } else {
            return getCount(rangeStart, mid, index * 2 + 1, treeStart, mid) + getCount(mid + 1, rangeEnd, index * 2 + 2, mid + 1, treeEnd);
        }
    }

    private void add(int rank, int index, int start, int end) {
        if (start == end) {
            tree[index]++;
            return;
        }
        int mid = start + (end - start) / 2;
        if (rank <= mid) {
            add(rank, index * 2 + 1, start, mid);
        } else {
            add(rank, index * 2 + 2, mid + 1, end);
        }
        tree[index] = tree[index * 2 + 1] + tree[index * 2 + 2];
    }
}

最后

看在博主这么努力,熬夜肝的情况下,给个免费的三连吧!
请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/333414.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android Jetpack组件之WorkManager后台任务管理的介绍与使用(一)

一、介绍 Jetpack&#xff1f; 我们经常看到&#xff0c;似乎很高端&#xff0c;其实这个就是一个名词&#xff0c;或者说是一种框架概念。 Jetpack 包含一系列 Android 库&#xff0c;它们都采用最佳做法并在 Android 应用中提供向后兼容性。 Jetpack 应用架构指南概述了构…

不要忽视web渗透测试在项目中起到的重要性

在当前数字化环境中&#xff0c;IT的一个里程碑式增长便是公司组织和企业数字化。为了扩大市场范围和方便业务&#xff0c;许多组织都在转向互联网。这导致了一股新的商业浪潮&#xff0c;它创造了网络空间中的商业环境。通过这种方式&#xff0c;公司和客户的官方或机密文件都…

Reflections反射包在springboot jar环境下扫描不到class排查过程

需求&#xff1a; 要实现指定pkg&#xff08;如com.qiqitrue.test.pojo&#xff09;扫描包下所有class类信息&#xff1a;使用代码如下 使用的版本&#xff1a;0.10.2&#xff08;截至目前是最新版&#xff09;发现只能在idea编译期间可以获取得到&#xff08;也就是在开发阶段…

项目管理的十条成功经验(建议收藏)

项目管理的十条成功经验 1、定义项目成功的标准 在项目的开始&#xff0c;要保证各方对于判断项目是否成功有统一的标准。 2、把握各种要求之间的平衡 每个项目都需要平衡它的功能、人员、预算、进度和质量目标。 3、部门客户间的沟通 坦诚地与客户、管理人员沟通那些实…

无需注册即可免费使用ChatGPT

无需注册即可免费使用ChatGPT 最近OpenAI的ChatGPT异常火爆&#xff0c;有很多人都想尝试尝试&#xff0c;但是因为一些原因折戟&#xff0c;这里提供一个免注册的体验方法&#xff0c;仅供学习交流。 一&#xff0c;首先下载vscode 官网下载地址 Visual Studio Code - Code…

文本匹配SimCSE模型代码详解以及训练自己的中文数据集

前言 在上一篇博客文本匹配中的示例代码中使用到了一个SimCSE模型&#xff0c;用来提取短文本的特征&#xff0c;然后计算特征相似度&#xff0c;最终达到文本匹配的目的。但是该示例代码中的短文本是用的英文短句&#xff0c;其实SimCSE模型也可以用于中文短文本的特征提取&a…

使用STM32 CUBE IDE配置STM32F7 用DMA传输多通道ADC数据

我的使用环境&#xff1a; 硬件&#xff1a;STM32F767ZGT6、串口1、ADC1、16MHz晶振、216MHz主频 软件&#xff1a;STM32 CUBE IDE 优点&#xff1a;不用定时触发采样&#xff0c;ADC数据是不停的实时更新&#xff0c;ADC数据的更新频率根据采样时钟和采样周期决定&#xff0c;…

负载均衡反向代理下的webshell上传+apache漏洞

目录一、负载均衡反向代理下的webshell上传1、nginx 负载均衡2、搭建环境3、负载均衡下的 WebShell连接的难点总结难点一、需要在每一台节点的相同位置都上传相同内容的 WebShell难点二、无法预测下次的请求交给哪台机器去执行。难点三、下载文件时&#xff0c;可能会出现飘逸&…

面试题:Redis的内存策略

1 Redis内存回收Redis之所以性能强&#xff0c;主要原因是基于内存存储&#xff0c;然而单节点的Redis内存不易过大&#xff0c;会影响主从同步和持久化性能我们可以通过修改配置文件设置Redis的最大内存&#xff1a;当内存存储到上限时&#xff0c;就无法存储更多的数据了。1.…

html控件Aspose.Html for .NET 授权须知

Aspose.Html for .NET是一种高级的HTML操作API&#xff0c;可让您直接在.NET应用程序中执行广泛的HTML操作任务&#xff0c;Aspose.Html for .NET允许创建&#xff0c;加载&#xff0c;编辑或转换&#xff08;X&#xff09;HTML文档&#xff0c;而无需额外的软件或工具。API还为…

Java开发学习(四十六)----MyBatisPlus新增语句之id生成策略控制及其简化配置

在前面有一篇博客&#xff1a;Java开发学习(四十一)----MyBatisPlus标准数据层&#xff08;增删查改分页&#xff09;开发&#xff0c;我们在新增的时候留了一个问题&#xff0c;就是新增成功后&#xff0c;主键ID是一个很长串的内容。 我们更想要的是按照数据库表字段进行自增…

CleanMyMac X4.12新版本下载及功能介绍

CleanMyMac X2023最新版终于迎来了又4.12&#xff0c;重新设计了 UI 元素&#xff0c;华丽的现代化风格显露无余。如今的CleanMyMac&#xff0c;早已不是单纯的系统清理工具。在逐渐融入系统优化、软件管理、文件管理等功能后&#xff0c;逐渐趋近于macOS的系统管家&#xff0c…

Python数据可视化(三)(pyecharts)

分享一些python-pyecharts作图小技巧&#xff0c;用于展示汇报。 一、特点 任何元素皆可配置pyecharts只支持python原生的数据类型&#xff0c;包括int,float,str,bool,dict,list动态展示&#xff0c;炫酷的效果&#xff0c;给人视觉冲击力 # 安装 pip install pyecharts fr…

算法训练营DAY51|300.最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组

本期是求子序列的新的一期&#xff0c;题目前两道有一些相似之处&#xff0c;思路差不多&#xff0c;第三道有一点难度&#xff0c;但并不意味着第一道没有难度&#xff0c;没有做过该类型题的选手&#xff0c;并不容易解出题解。 300. 最长递增子序列 - 力扣&#xff08;Leet…

22级浙江大学MBA笔试备考的若干经验分享

我是浙江大学2022级的一名新生&#xff0c;虽然没有参加提前批面试&#xff0c;但是通过笔试的有序备考最终也有幸上岸浙大&#xff0c;对于部分提前批面试没拿到优秀资格的考友&#xff0c;今天我想把自己的笔试上岸经验做个总结&#xff0c;给大家提供一个参考模版。 先…

TC358774XBG/TC358775XBG替代方案|CS5518替代TC358774XBG/TC358775XBG设计DSI转LVSD设计资料

TC358774XBG/TC358775XBG替代方案|CS5518替代TC358774XBG/TC358775XBG设计DSI转LVSD设计资料 TC358774XBG/TC358775XBG 芯片的主要功能是作为 DSI - LVDS 通信协议桥接&#xff0c;主芯片的视频数据可通过 DSI 链路流 出&#xff0c;以驱动兼容 LVDS 的显示板。换句话说&#x…

百度官宣在前,阿里、京东在后,互联网大厂向ChatGPT而生?

ChatGPT蹿红后&#xff0c;互联网科技公司都坐不住了。 最早&#xff0c;百度正式对外官宣类ChatGPT项目“文心一言”&#xff08;ERNIE Bot&#xff09;。据笔者了解&#xff0c;该产品将于三月份完成内测&#xff0c;面向公众开放。 紧随其后&#xff0c;阿里巴巴公布阿里版…

流浪地球 | 建筑人是如何看待小破球里的黑科技的?

大家好&#xff0c;这里是建模助手。 想问问大家今年贺岁档&#xff0c;都跟上没有&#xff0c;今天请允许我蹭一下热点表达一下作为一个科幻迷的爱国之情。 抛开大刘的想象力、各种硬核科技&以及大国情怀不提&#xff0c;破球2中的传承还是让小编很受感动&#xff0c;无…

【2023】Prometheus-Prometheus与Alertmanager配置详解

记录一下Prometheus与Alertmanager的配置参数等内容 目录1.Prometheus1.1.prometheus.yml1.2.告警规则定义2.alertmanager2.1.alertmanager.yml2.1.1.global&#xff1a;全局配置2.1.1.1.以email方式作为告警发送方2.1.1.2.以wechat方式作为告警发送方2.1.1.3.以webhook方式作为…

c++基础入门二

一、数组的引用int main() {int a 10, b 20;int ar[10] { 1,2,3,4,6,7 };int& x ar[0];int& p[5] ar;//errorint(&p)[10] ar;//引用整个数组的大小sizeof(ar)int(*p)[10] &ar;//typesize表示整个数组//只有在这三种情况下代表整个数组&#xff0c;其他情…