基于C#实现树状数组

有一种数据结构是神奇的,神秘的,它展现了位运算与数组结合的神奇魅力,太牛逼的,它就是树状数组,这种数据结构不是神人是发现不了的。

一、概序

假如我现在有个需求,就是要频繁的求数组的前 n 项和,并且存在着数组中某些数字的频繁修改,那么我们该如何实现这样的需求?当然大家可以往真实项目上靠一靠。
**① 传统方法:**根据索引修改为 O(1),但是求前 n 项和为 O(n)。
**② 空间换时间方法:**我开一个数组 sum[],sum[i]=a[1]+…+a[i],那么有点意思,求 n 项和为 O(1),但是修改却成了 O(N),这是因为我的 Sum[i]中牵涉的数据太多了,那么问题来了,我能不能在相应的 sum[i]中只保存某些 a[i]的值呢?好吧,下面我们看张图。
image.png
从图中我们可以看到 S[]的分布变成了一颗树,有意思吧,下面我们看看 S[i]中到底存放着哪些 a[i]的值。

S[1]=a[1];
S[2]=a[1]+a[2];
S[3]=a[3];
S[4]=a[1]+a[2]+a[3]+a[4];
S[5]=a[5];
S[6]=a[5]+a[6];
S[7]=a[7];
S[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8];

之所以采用这样的分布方式,是因为我们使用的是这样的一个公式:S[i]=a[i-2k+1]+…+a[i]。
其中:2k 中的 k 表示当前 S[i]在树中的层数,它的值就是 i 的二进制中末尾连续 0 的个数,2k 也就是表示 S[i]中包含了哪些 a[],举个例子: i=610=01102 ;可以发现末尾连续的 0 有一个,即 k=1,则说明 S[6]是在树中的第二层,并且 S[6]中有 21 项,随后我们求出了起始项:
a[6-21+1]=a[5],但是在编码中求出 k 的值还是有点麻烦的,所以我们采用更灵巧的 Lowbit 技术,即:2k=i&-i 。
则:S[6]=a[6-21+1]=a[6-(6&-6)+1]=a[5]+a[6]。

二、代码

1、神奇的 Lowbit 函数

 #region 当前的sum数列的起始下标/// <summary>/// 当前的sum数列的起始下标/// </summary>/// <param name="i"></param>/// <returns></returns>public static int Lowbit(int i){return i & -i;}#endregion

2、求前 n 项和

比如上图中,如何求 Sum(6),很显然 Sum(6)=S4+S6,那么如何寻找 S4 呢?即找到 6 以前的所有最大子树,很显然这个求和的复杂度为 logN。

 #region 求前n项和/// <summary>/// 求前n项和/// </summary>/// <param name="x"></param>/// <returns></returns>public static int Sum(int x){int ans = 0;var i = x;while (i > 0){ans += sumArray[i - 1];//当前项的最大子树i -= Lowbit(i);}return ans;}#endregion

3、修改

如上图中,如果我修改了 a[5]的值,那么包含 a[5]的 S[5],S[6],S[8]的区间值都需要同步修改,我们看到只要沿着 S[5]一直回溯到根即可,同样它的时间复杂度也为 logN。

 public static void Modify(int x, int newValue){//拿出原数组的值var oldValue = arr[x];for (int i = x; i < arr.Length; i += Lowbit(i + 1)){//减去老值,换一个新值sumArray[i] = sumArray[i] - oldValue + newValue;}}

最后上总的代码:

 using System;using System.Collections.Generic;using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.IO;namespace ConsoleApplication2
{public class Program{static int[] sumArray = new int[8];static int[] arr = new int[8];public static void Main(){Init();Console.WriteLine("A数组的值:{0}", string.Join(",", arr));Console.WriteLine("S数组的值:{0}", string.Join(",", sumArray));Console.WriteLine("修改A[1]的值为3");Modify(1, 3);Console.WriteLine("A数组的值:{0}", string.Join(",", arr));Console.WriteLine("S数组的值:{0}", string.Join(",", sumArray));Console.Read();}#region 初始化两个数组/// <summary>/// 初始化两个数组/// </summary>public static void Init(){for (int i = 1; i <= 8; i++){arr[i - 1] = i;//设置其实坐标:i=1开始int start = (i - Lowbit(i));var sum = 0;while (start < i){sum += arr[start];start++;}sumArray[i - 1] = sum;}}#endregionpublic static void Modify(int x, int newValue){//拿出原数组的值var oldValue = arr[x];arr[x] = newValue;for (int i = x; i < arr.Length; i += Lowbit(i + 1)){//减去老值,换一个新值sumArray[i] = sumArray[i] - oldValue + newValue;}}#region 求前n项和/// <summary>/// 求前n项和/// </summary>/// <param name="x"></param>/// <returns></returns>public static int Sum(int x){int ans = 0;var i = x;while (i > 0){ans += sumArray[i - 1];//当前项的最大子树i -= Lowbit(i);}return ans;}#endregion#region 当前的sum数列的起始下标/// <summary>/// 当前的sum数列的起始下标/// </summary>/// <param name="i"></param>/// <returns></returns>public static int Lowbit(int i){return i & -i;}#endregion}
}

image.png

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

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

相关文章

Day29| Leetcode 491. 递增子序列 Leetcode 46. 全排列 Leetcode 47. 全排列 II

Leetcode 491. 递增子序列 题目链接 491 递增子序列 本题目和前面的树层去重不太一样&#xff0c;因为这里不能对原集合进行排序&#xff0c;所以之前的去重就没法使用了&#xff0c;只能单纯的记录该元素有没有使用过&#xff08;去重&#xff09;&#xff0c;下面使用的是…

Redis事务的理解与使用

文章目录 Redis 事务1)基本认识2)事务操作1.MULTI2.EXEC3.错误处理4.DISCARD5.WATCH6.SCRIPT Redis 事务 官方文档&#xff0c;永远是你学习的第一手资料&#xff1a;Redis 事务 1)基本认识 谈到事务&#xff0c;大家首先都会联想到 mysql 中复杂但又功能强大的“事务”&…

MySQL面试题总结

1. 表之间如何关联 表与表之间常用的关联方式有两种&#xff1a;内连接、外连接&#xff0c;下面以MySQL为例来说明这两种连接方式 内连接通过INNER JOIN来实现&#xff0c;它将返回两张表中满足连接条件的数据&#xff0c;不满足条件的数据不会查询出来 外连接 外连接通过OUTE…

SpringBoot——感谢尚硅谷官方文档

SpringBoot——感谢尚硅谷官方文档 1 Spring与SpringBoot1、Spring能做什么1.1、Spring的能力1.2、Spring的生态1.3、Spring5重大升级1.3.1、响应式编程1.3.2、内部源码设计 2、为什么用SpringBoot2.1、SpringBoot优点2.2、SpringBoot缺点 3、时代背景3.1、微服务3.2、分布式分…

Dubbo从入门到上天系列第十八篇:Dubbo引入Zookeeper等注册中心简介以及DubboAdmin简要介绍,为后续详解Dubbo各种注册中心做铺垫!

文章目录 一&#xff1a;Dubbo注册中心引言 1&#xff1a;什么是Dubbo的注册中心&#xff1f; 2&#xff1a;注册中心关系图解 3&#xff1a;引入注册中心服务执行流程 4&#xff1a;Dubbo注册中心好处 5&#xff1a;注册中心核心作用 二&#xff1a;注册中心实现方案 …

10.docker的网络network-概述

1.docker的网络模式 docker共有四种网路模式&#xff0c;分别是bridge、host、none和container. 1.1 bridge bridge,也称为虚拟网桥。在bridge模式下&#xff0c;为每个容器分配、配置IP等&#xff0c;并将容器连接到一个docker0。使用–network bridge命令指定&#xff0c;…

【01】ES6:ECMAScript 介绍

ECMAScript 6.0 &#xff08;以下简称 ES6&#xff09;是 JavaScript 语言的下一代标准&#xff0c;已经在 2015 年 6 月正式发布。它的目标&#xff0c;是使得 JavaScript 语言可以用来编写复杂的大型应用程序&#xff0c;成为企业级开发语言。 参考&#xff1a;《ECMAScript…

C# Winform使用log4net记录日志

写在前面 Log4Net是从Java的log4j移植过来的&#xff0c;功能也与log4j类似&#xff0c;可以把日志信息输出到文件、数据库、控制台、Windows 事件日志、远程系统日志服务等不同的介质或目标。 Log4Net配置选项丰富灵活&#xff0c;并且可在运行时动态更新配置并应用&#xf…

解析紫光展锐T820 5G芯片——让照片接近原色

紫光展锐系统级安全的高性能5G SoC芯片平台T820&#xff0c;采用八核CPU架构&#xff0c;6nm EUV先进工艺&#xff0c;金融级全内置安全方案&#xff0c;在性能、功耗与5G通信体验等方面&#xff0c;较上一代产品更为出色。 此前&#xff0c;已经为大家讲解过T820的拍照、安全性…

mac跑分工具 Geekbench v6.2.2

Geekbench 6 是一款跨平台的系统性能测试软件&#xff0c;可以对处理器和内存等硬件进行评测&#xff0c;并提供了单核和多核两种测试模式。该软件适用于 Windows、macOS、Linux 和 iOS 等多种操作系统平台。 Geekbench 6 测试可以帮助用户快速准确地了解自己设备的性能表现&am…

【brpc学习实践五】brpc自适应限流案例

自适应限流 服务的处理能力是有客观上限的。当请求速度超过服务的处理速度时&#xff0c;服务就会过载。 如果服务持续过载&#xff0c;会导致越来越多的请求积压&#xff0c;最终所有的请求都必须等待较长时间才能被处理&#xff0c;从而使整个服务处于瘫痪状态。 与之相对…

PHP中间件实现

目录 1、简单中间实现 2、使用闭包函数实现中间件 在PHP中&#xff0c;中间件是一种常用的设计模式&#xff0c;用于处理请求和响应&#xff0c;它可以在请求到达目标处理程序之前或响应发送给客户端之前执行一些特定的逻辑。中间件提供了一种灵活的方式来修改或扩展应用程序的…

查看当前laravel版本三种方法(笔记二)

1、在终端中使用 Artisan 命令&#xff1a;在 Laravel 项目的根目录下&#xff0c;打开终端&#xff08;命令行界面&#xff09;&#xff0c;然后运行以下命令&#xff1a; php artisan --version 2、控制器中打印版本 var_dump(app()->version()); 3、在 Laravel 项目的根目…

【kubernetes】k8s架构之节点

文章目录 1、集群架构示意图2、概述3、管理3.1 节点名称唯一性3.2 节点自注册3.3 手动节点管理 4、节点状态4.1 地址&#xff08;Addresses&#xff09;4.2 状况&#xff08;Condition&#xff09;4.3 容量&#xff08;Capacity&#xff09;与可分配&#xff08;Allocatable&am…

PTA-输出三角形面积和周长

本题要求编写程序&#xff0c;根据输入的三角形的三条边a、b、c&#xff0c;计算并输出面积和周长。注意&#xff1a;在一个三角形中&#xff0c; 任意两边之和大于第三边。三角形面积计算公式&#xff1a;areas(s−a)(s−b)(s−c)​&#xff0c;其中s(abc)/2。 输入格式&…

某60区块链安全之Call函数簇滥用实战二学习记录

区块链安全 文章目录 区块链安全Call函数簇滥用实战二实验目的实验环境实验原理实验内容实验步骤EXP利用 Call函数簇滥用实战二 实验目的 学会使用python3的web3模块 学会并区分以太坊call、staticcall、delegatecall三种函数调用的特点 找到合约漏洞进行分析并形成利用 实验…

关于git hooks

Git hooks 是一种在 Git 仓库中触发自定义脚本的机制。这些脚本可以在特定的 Git 操作&#xff08;如提交、推送、合并等&#xff09;发生时执行。通过使用 Git hooks&#xff0c;你可以在版本控制的不同阶段自动运行脚本&#xff0c;以执行一些定制化的操作。 在 Git 中&…

03梯度下降

目录 lambda基础知识 代码 核心算法&#xff1a; lambda基础知识 lambda 是 Python 中的一个关键字&#xff0c;用于创建匿名函数。匿名函数是一种没有具体名称的小型、临时的函数&#xff0c;通常用于一次性的、简单的操作。lambda 函数的语法如下&#xff1a;python Copy c…

高效运维工具,助力运维服务商为企业用户提供IT远程维保服务

一、背景介绍 随着科技的迅速发展和信息化建设的不断推进&#xff0c;IT运维在中小企业中的地位逐渐提升。IT运维是指通过技术手段和工具&#xff0c;对企业的IT基础设施进行监控、管理和维护&#xff0c;以确保企业信息系统的稳定运行和业务的持续发展。 然而&#xff0c;对于…

计算3个点的6种分布在平面上的占比

假设平面的尺寸是6*6&#xff0c;用11的方式构造2&#xff0c;在用21的方式构造3 2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 1 2 2 3 3 3 x 3 3 2 2 2 1 2 2 2 2 2 1 2 2 在平面上有一个点x&#xff0c;11的操作吧平面分成了3部分2a1&#xff0c;2a…