0基础学习线段树

前言:

线段树:用树来表示一个一个的线段区间。

1、为什么要使用线段树?

题目:给定一个数组nums,我们有两种下面两种操作

1、查询nums数组下标i到下标j的和;

2、将nums数组指定下标的值改为指定的一个新值;

如果上面两种操作频繁交叉进行,如何使整体效率更高。

方案一:更新操作O1

每次查询操作都从i加到j,每次更新直接更新对应下标。

缺点:如果每次查询操作都是查整个数组的和,一下子来了几万个查询操作,则每次都是O(n),几万次O(n),比较浪费时间。

方案二:查询操作O1

新建一个同等大小数组dp,dp【i】用于记录从nums[0]加到nums[i]的值。

缺点:虽然查询操作从O(n),变成O1(dp[j]-dp[i-1])。但是更新操作需要变成了O(n),每次更新都需要维护dp数组,从更新处开始往后面的值都需要重新计算,如果一次性来了大量的更新操作请求,则比较浪费时间。

方案三:线段树(查询Ologn,更新Ologn)

2、线段树为什么开4n空间?

1、假设n刚好是2的整数次阶乘,则n的满二叉树具有下面特征:

1)树的层高:h=log2^n+1;

2)树的最后一层的节点数:an=2^(h-1);

3)树的节点数总和:sn=2n-1;

那为什么要开4n空间呢?因为如果n=9,呢?即n不是刚好2的某个整数的阶乘,这时候会在满二叉树的基础上多出一层来。

多出来一层的节点数为:an+1=2^h;

那么树的节点数总和就变成了:sn=2n-1+2^h,h=log2^n+1,所以:sn=4n-1。

因为存储的时候下标从1开始,所以我们给4n的空间。

3、构造线段树

1、节点数组下标分配:从上到下,从左到右

2、父子节点间下标关系:

l = fa*2 (左子树下标为父节点下标的两倍)
r = fa*2+1(右子树下标为父节点下标的两倍+1)

3、代码如下:

class Tree{int[] nums;int[] tree;public Tree(int[] nums){this.nums = nums;int n = nums.length;tree = new int[4*n];build(nums,0,0,0);}
//	l = fa*2 (左子树下标为父节点下标的两倍)
//	r = fa*2+1(右子树下标为父节点下标的两倍+1)	public void build(int[] nums,int id,int l,int r){if(l==r){tree[id] = nums[l];return;}int mid = (l + r)/2;build(nums,2*id,l,mid);build(nums,2*id+1,mid,r);tree[id] = tree[2*id]+tree[2*id+1];			}
}

4、线段树区间查询

我们知道线段树的每个结点存储的都是一段区间的信息 ,如果我们刚好要查询这个区间,那么则直接返回这个结点的信息即可,比如对于上面线段树,如果我直接查询[1,6]这个区间的最值,那么直接返回根节点信息返回13即可,但是一般我们不会凑巧刚好查询那些区间,比如现在我要查询[2,5]区间的最值,这时候该怎么办呢,我们来看看哪些区间被[2,5]包含了。

一共有5个区间,而且我们可以发现[4,5]这个区间已经包含了两个子树的信息([4,4],[5,5]),所以我们需要查询的区间只有三个,分别是[2,2],[3,3],[4,5],我们从根节点开始往下递归,如果当前结点是被要查询的区间包含了的,则返回这个结点的信息,这样从根节点往下递归,时间复杂度也是O(logN)。

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/weq2011/article/details/128791426

//	查询指定区间内的值的和public int find(int id,int l,int r,int x,int y){
//		当区间完全在要求查找区间范围内时,就是我们要的值if(l>=x && r<=y){return tree[id];}int mid = (l + r)/2;int sum = 0;
//		如果值存在于左子树,则需要进行查找if(x<=mid){sum+=find(2*id,l,mid,x,y);}
//		如果值存在于右子树,则需要进行查找if(y>mid){sum+=find(2*id+1,mid+1,r,x,y);}return sum;}

5、线段树更新

id:从线段树哪个下标开始检索

l:数值区间左

r:数值区间右

index:需要更新的dp下标

val:更新的值

//	更新指定下标的值
//	指定下标public void update(int id,int l,int r,int index,int val){tree[id] = tree[id]+val;if(l == index && r == index){return;}int mid = (l + r)/2;if(index<=mid){update(2*id,l,mid,index,val);}if(index>mid){update(2*id+1,mid+1,r,index,val);}}

6、完整案例及代码

3187. 数组中的峰值

数组 arr 中 大于 前面和后面相邻元素的元素被称为 峰值 元素。

给你一个整数数组 nums 和一个二维整数数组 queries 。

你需要处理以下两种类型的操作:

  • queries[i] = [1, li, ri] ,求出子数组 nums[li..ri] 中 峰值 元素的数目。
  • queries[i] = [2, indexi, vali] ,将 nums[indexi] 变为 vali 。

请你返回一个数组 answer ,它依次包含每一个第一种操作的答案。

注意:

  • 子数组中 第一个 和 最后一个 元素都 不是 峰值元素。

 

示例 1:

输入:nums = [3,1,4,2,5], queries = [[2,3,4],[1,0,4]]

输出:[0]

解释:

第一个操作:我们将 nums[3] 变为 4 ,nums 变为 [3,1,4,4,5] 。

第二个操作:[3,1,4,4,5] 中峰值元素的数目为 0 。

示例 2:

输入:nums = [4,1,4,2,1,5], queries = [[2,2,4],[1,0,2],[1,0,4]]

输出:[0,1]

解释:

第一个操作:nums[2] 变为 4 ,它已经是 4 了,所以保持不变。

第二个操作:[4,1,4] 中峰值元素的数目为 0 。

第三个操作:第二个 4 是 [4,1,4,2,1] 中的峰值元素。 

提示:

  • 3 <= nums.length <= 105
  • 1 <= nums[i] <= 105
  • 1 <= queries.length <= 105
  • queries[i][0] == 1 或者 queries[i][0] == 2
  • 对于所有的 i ,都有:
    • queries[i][0] == 1 :0 <= queries[i][1] <= queries[i][2] <= nums.length - 1
    • queries[i][0] == 2 :0 <= queries[i][1] <= nums.length - 11 <= queries[i][2] <= 105

1)线段树求解代码: 

package algorithm.temp;import java.util.ArrayList;
import java.util.List;class Test2 {static final int COUNT_PEAK = 1, UPDATE = 2;public static void main(String[] args) {int[][] queyy = new int[4][3];queyy[0][0] = 2;queyy[0][1] = 0;queyy[0][2] = 2;queyy[1][0] = 1;queyy[1][1] = 0;queyy[1][2] = 3;//		queyy[0][0] = 1;
//		queyy[0][1] = 2;
//		queyy[0][2] = 4;
//		queyy[1][0] = 1;
//		queyy[1][1] = 0;
//		queyy[1][2] = 1;queyy[2][0] = 1;queyy[2][1] = 3;queyy[2][2] = 3;queyy[3][0] = 2;queyy[3][1] = 3;queyy[3][2] = 5;System.out.println(countOfPeaks(new int[] {9,7,5,8,9}, queyy));;}public static List<Integer> countOfPeaks(int[] nums, int[][] queries) {List<Integer> li = new ArrayList<Integer>();int[] dp = new int[nums.length];dp[0] = 0;for(int i=1;i<nums.length;i++){if(isTopElement(nums,i)){dp[i]= 1;dp[++i] = 0;};}Tree tree = new Tree(dp);for(int i=0;i<queries.length;i++){int[] temp = queries[i];if(temp[0] == 1){int x = temp[1];int y = temp[2];li.add(y-x>1?tree.find(1, 0, dp.length-1, x+1, y-1):0);}else{int index = temp[1];int val = temp[2];nums[index] = val;for(int k=-1;k<=1;k++){int num=0;if(index+k>0 && index+k<nums.length-1){num=isTopElement(nums,index+k)?1-dp[index+k]:0-dp[index+k];tree.update(1,0, dp.length-1, index+k, num);dp[index+k] = dp[index+k]+num;}}}}return li;}public static boolean isTopElement(int[] nums,int index) {if(index-1>=0 && index+1<=nums.length-1 && nums[index]>nums[index-1] && nums[index]>nums[index+1]){return true;}return false;}}class Tree{int[] nums;int[] tree;public Tree(int[] nums){this.nums = nums;int n = nums.length;tree = new int[4*n];build(nums,1,0,n-1);}
//	l = fa*2 (左子树下标为父节点下标的两倍)
//	r = fa*2+1(右子树下标为父节点下标的两倍+1)	public void build(int[] nums,int id,int l,int r){if(l==r){tree[id] = nums[l];return;}int mid = (l + r)/2;build(nums,2*id,l,mid);build(nums,2*id+1,mid+1,r);tree[id] = tree[2*id]+tree[2*id+1];			}
//	查询指定区间内的值的和public int find(int id,int l,int r,int x,int y){
//		当区间完全在要求查找区间范围内时,就是我们要的值if(l>=x && r<=y){return tree[id];}int mid = (l + r)/2;int sum = 0;
//		如果值存在于左子树,则需要进行查找if(x<=mid){sum+=find(2*id,l,mid,x,y);}
//		如果值存在于右子树,则需要进行查找if(y>mid){sum+=find(2*id+1,mid+1,r,x,y);}return sum;}
//	更新指定下标的值
//	指定下标public void update(int id,int l,int r,int index,int val){tree[id] = tree[id]+val;if(l == index && r == index){return;}int mid = (l + r)/2;if(index<=mid){update(2*id,l,mid,index,val);}if(index>mid){update(2*id+1,mid+1,r,index,val);}}
}

2)暴力求解代码

(执行效率低下,本人第一次就是写的这段代码,案例跑超时):

class Solution {public List<Integer> countOfPeaks(int[] nums, int[][] queries) {List<Integer> li = new ArrayList<Integer>();int[] dp = new int[nums.length];dp[0] = 0;for(int i=1;i<nums.length;i++){if(isTopElement(nums,i)){dp[i]= 1;dp[++i] = 0;};}for(int i=0;i<queries.length;i++){int[] temp = queries[i];if(temp[0] == 1){int sum = 0;for(int k=temp[1]+1;k<temp[2]&&k<nums.length;k++){sum+=dp[k];}li.add(sum);}else{nums[temp[1]] = temp[2];for(int k=temp[1]-1;k<=temp[1]+1;k++){if(isTopElement(nums,k)){dp[k] = 1;}else if(k>0&&k<nums.length){dp[k] = 0;}}}}return li;}public boolean isTopElement(int[] nums,int index) {if(index-1>=0 && index+1<=nums.length-1 && nums[index]>nums[index-1] && nums[index]>nums[index+1]){return true;}return false;}
}

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

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

相关文章

动手学深度学习(Pytorch版)代码实践 -卷积神经网络-28批量规范化

28批量规范化 """可持续加速深层网络的收敛速度""" import torch from torch import nn import liliPytorch as lp import matplotlib.pyplot as pltdef batch_norm(X, gamma, beta, moving_mean, moving_var, eps, momentum):""&quo…

Swift 中的动态数组

Swift 的 Array 类型是一种强大而灵活的集合类型&#xff0c;可以根据需要自动扩展或缩减其容量。 动态数组的基本概念 Swift 中的数组是基于动态数组&#xff08;dynamic array&#xff09;的概念实现的。动态数组能够根据需要自动调整其容量&#xff0c;以容纳新增的元素&a…

Benchmarking Panoptic Scene Graph Generation (PSG), ECCV‘22 场景图生成,利用PSG数据集

2080-ti显卡复现 源代码地址 Jingkang50/OpenPSG: Benchmarking Panoptic Scene Graph Generation (PSG), ECCV22 (github.com) 安装 pytorch 1.7版本 cuda10.1 按照readme的做法安装 我安装的过程如下图所示,这个截图是到了pip install openmim这一步 下一步 下一步 这一步…

C语言 | Leetcode C语言题解之第167题两数之和II-输入有序数组

题目&#xff1a; 题解&#xff1a; int* twoSum(int* numbers, int numbersSize, int target, int* returnSize) {int* ret (int*)malloc(sizeof(int) * 2);*returnSize 2;int low 0, high numbersSize - 1;while (low < high) {int sum numbers[low] numbers[high]…

如何设置MySQL远程访问权限?

MySQL是一种流行的关系型数据库管理系统&#xff0c;它广泛应用于各种Web应用程序和数据驱动的应用中。在默认情况下&#xff0c;MySQL只允许本地访问&#xff0c;为了能够从远程服务器或客户端访问MySQL数据库&#xff0c;我们需要进行一些额外的设置和配置。 安装和配置MySQ…

在阿里云使用Docker部署MySQL服务,并且通过IDEA进行连接

阿里云使用Docker部署MySQL服务&#xff0c;并且通过IDEA进行连接 这里演示如何使用阿里云来进行MySQL的部署&#xff0c;系统使用的是Linux系统 (Ubuntu)。 为什么使用Docker? 首先是因为它的可移植性可以在任何有Docker环境的系统上运行应用&#xff0c;避免了在不通操作系…

【html】用html+css实现银行的账户信息表格

我们先来看一看某银行的账户信息表格 我们自己也可以实现类似的效果 效果图: 大家可以看到&#xff0c;其实效果差不多 接下来看看我们实现的代码 源码&#xff1a; <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"&…

机械师硬盘数据清空怎么办?机械师硬盘数据清空怎么恢复

机械师硬盘数据清空怎么恢复&#xff1f;随着数字化时代的到来&#xff0c;数据已成为我们生活和工作中不可或缺的一部分。然而&#xff0c;硬盘数据的意外清空往往会给我们带来极大的困扰。本文将探讨在机械师硬盘数据清空后&#xff0c;我们应该如何快速有效地恢复数据。 图片…

二本毕业,我是如何成为BAT-安卓开发工程师?

1.对基础原理不断挖掘 进入公司&#xff0c;我的职位是Linux应用开发工程师&#xff0c;做App网络传输模块&#xff0c;本质上就是把本地的数据通过socket传输到服务端。用到的技术是C语言&#xff0c;网络编程&#xff0c;多线程编程。 那时是最痛苦的几个月&#xff0c;因为…

[FreeRTOS 功能应用] 互斥访问与回环队列 功能应用

文章目录 一、基础知识点二、代码讲解三、结果演示四、代码下载 一、基础知识点 [FreeRTOS 基础知识] 互斥访问与回环队列 概念 [FreeRTOS 内部实现] 互斥访问与回环队列 [FreeRTOS 内部实现] 创建任务 xTaskCreate函数解析 本实验是基于STM32F103开发移植FreeRTOS实时操作系…

解决WebStorm中不显示npm任务面板

鼠标右键项目的package.json文件&#xff0c;然后点击show npm scripts选项。 然后npm工具窗口就显示了&#xff1a;

02--MySQL数据库概述

目录 第10章 子查询 10.1 SELECT的SELECT中嵌套子查询 10.2 SELECT的WHERE或HAVING中嵌套子查询 10.3 SELECT中的EXISTS型子查询 10.4 SELECT的FROM中嵌套子查询 第11章 MySQL支持的数据类型 11.1 数值类型:包括整数和小数 1、整数类型 2、bit类型 3、小数类型 11.2…

【Python系列】探索 NumPy 中的 mean 函数:计算平均值的利器

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

软件串口接收子程序

代码; stduart.c /*《AVR专题精选》随书例程3.通信接口使用技巧项目&#xff1a;使用延时法实现半双工软件串口文件&#xff1a;sfuart.c说明&#xff1a;软件串口驱动文件作者&#xff1a;邵子扬时间&#xff1a;2012年12月13日*/ #include "sfduart.h"// 循环中延…

WinMerge v2 (开源的文件比较/合并工具)

前言 WinMerge 是一款运行于Windows系统下的免费开源的文件比较/合并工具&#xff0c;使用它可以非常方便地比较多个文档内容甚至是文件夹与文件夹之间的文件差异。适合程序员或者经常需要撰写文稿的朋友使用。 一、下载地址 下载链接&#xff1a;http://dygod/source 点击搜…

【Linux】进程间通信3——线程安全

1.Linux线程互斥 1.1.进程线程间的互斥相关背景概念 临界资源&#xff1a; 多线程执行流共享的资源叫做临界资源。临界区&#xff1a; 每个线程内部&#xff0c;访问临界资源的代码&#xff0c;就叫做临界区。互斥&#xff1a; 任何时刻&#xff0c;互斥保证有且只有一个执行…

动态创建接口地址

和SpringBoot版本有关系 这里用的boot 2.2.2

LabVIEW电控旋翼测控系统

开发基于LabVIEW开发的电控旋翼测控系统&#xff0c;通过高效监控和控制提升旋翼系统的性能和安全性。系统集成了多种硬件设备&#xff0c;采用模块化设计&#xff0c;实现复杂的控制和数据处理功能&#xff0c;适用于现代航空航天领域。 项目背景 传统旋翼系统依赖机械和液压…

IO模型详解

阻塞IO模型 假设应用程序的进程发起IO调用&#xff0c;但是如果内核的数据还没准备好的话&#xff0c;那应用程序进程就一直在阻塞等待&#xff0c;一直等到内核数据准备好了&#xff0c;从内核拷贝到用户空间&#xff0c;才返回成功提示&#xff0c;此次IO操作&#xff0c;称…

C# 中的静态关键字

C# 语言中的 static 关键字用于声明静态类和静态类成员。静态类和静态类成员&#xff08;如构造函数、字段、属性、方法和事件&#xff09;在只需要一个对象&#xff08;类或类成员&#xff09;副本并在类型&#xff08;和成员&#xff09;的所有实例&#xff08;对象&#xff…