樹狀數組詳解

概述

樹狀數組(Binary Indexed Tree,簡稱BIT),是一種數據結構,用于處理區間查詢和更新問題。它是一種可以高效地在對數級別時間復雜度內進行單點更新和區間查詢的數據結構。樹狀數組通常用于解決以下兩類問題:

  1. 區間和查詢:給定一個序列,查詢序列中任意區間的和。
  2. 區間更新:給定一個序列,對序列中任意區間的值進行增加或減少。

問題引入

給定一個長度為n的數組,完成以下兩種操作:

  • 更新:將第x個數加上k;
  • 查詢:輸出區間[x, y]內每個數的和。

我們很容易想到一種樸素做法,更新操作直接在原數組上操作,查詢遍歷一下即可,對應的時間復雜度分別為O(1)和O(n)。

當然,你也可能想到用前綴和數組來優化,這樣的話更新操作的時間復雜度就是O(n),查詢操作的復雜度為O(1)。

可以發現,兩種做法中,要么查詢是O(1),更新是O(n);要么更新是O(1),查詢是O(n)。那么就有沒有一種做法可以綜合一下這兩種樸素做法,然后整體時間復雜度可以降一個數量級呢?有的,對,就是樹狀數組。

lowbit函數

學習樹狀數組之前首先需要了解一下lowbit函數。lowbit函數的功能就是求某一個數的二進制表示中最低的一位1所表示的數值。這個數值一定是2的冪。舉個例子,x = 6,它的二進制為110,那么lowbit(x)就返回2,因為最后一個1表示2。再舉個例子,lowbit(4) = 4

我們知道,負數的補碼是它的反碼+1。當然,還有一種快捷求法就是,從右往左數第一個1及其右邊的0不動,剩下的位取反。這時候,我們如果讓它和原數進行二進制與操作,就能得到最后的一個1及其后面的0。例如,6的二進制為0110,-6的補碼為1010,它們兩個做與運算就能消掉最后一個1前面的所有位。用代碼表示如下:

int lowbit(int x)
{return x & -x;
}

樹狀數組的思想

首先要明確樹狀數組里存的是什么。假設原數組是arr,我們需要維護一個新的樹狀數組cc數組里的每一位存的是arr中對應下標開始往前數lowbit(下標)個數的和。例如,c[6]的下標為6,并且lowbit(6) = 2,所以c[6]存的就是arr中從第6項開始往前數2個數的和,即arr[5] + arr[6]。因此,相比前綴和數組,樹狀數組可以說存的是區間和。

查詢

明白了樹狀數組存的是什么,就可以用樹狀數組來求前綴和了。因為查詢操作還是要通過兩個前綴和做差來得到任意區間的和。

因為樹狀數組存的是區間和,我們通過不同的區間拼湊出一個完整的前綴區間就能計算前綴和。還是以6為例,6的二進制為110,可以寫成100 + 10,即4 + 2。根據樹狀數組的定義,c[6]存的是arr[5] + arr[6]。得到第一個區間和后,減去lowbit,即6 - lowbit(6) = 6 - 2 = 4。而c[4]存的是arr中第1項到第4項的和,這是因為lowbit(4) = 4。這兩段拼起來正好得到第6項的前綴和。

因此,用樹狀數組求第x項前綴和可以用下面的代碼表示:

int sum(int x, int c[])
{int res = 0;for (; x > 0; x -= lowbit(x))res += c[x];return res;
}

更新

如果理解了上述過程,我們其實能發現,樹狀數組求前綴和本質上就是將下標展開成二進制,根據二進制位上的1來求和,從而實現對數級別的復雜度。樹狀數組用圖來表示就是像下面這樣。

其中,1到12是樹狀數組的下標,上面的橫條表示了這一項對應arr數組中的區間和。我們從這張圖中可以得到樹狀數組的如下性質:

  • 下面層的下標只要補上自己的lowbit值就可以得到上面層的下標(圖中的虛線指出了什么是上面層)。注意,是上面層的下標,而不是上一層的下標,這個性質就是更新操作的依據;

例如,下標6只要加上lowbit(6),也就是2,就能跳到自己的上面層,也就是8。之所以8是上面層,是因為它們的區間產生了重疊。加上lowbit值會讓最低位的1往高位移動,其所代表的冪會指數增長,遠大于加上的值。所以上面層必定包含下面層。如果不能理解記住這個性質就好。

理解了這一點,就可以明白更新操作了。如果在arr數組上進行更新操作,很簡單,只要修改第x項就可以了。但是樹狀數組表示的是區間和,修改了這一項會影響到很多包含這一項的區間。因此,在c數組上,所有包含第x項的區間和都要修改。

更新了arr的第x項,首先影響到的就是c[x]。因為c[x]所代表的區間和長度至少為1,即必定包含arr[x]。然后就是上面的性質所說的上面層了。我們通過不斷加上lowbit值往上層跳,不斷更新c數組,就能實現對數級別復雜度的更新操作。代碼如下:

void update(int x, int val, int c[], int n)
{for (; x <= n; x += lowbit(x))c[x] += val;
}

代碼實現

輸入格式

第一行輸入兩個整數n和m,分別表示數組長度和操作的次數;

第二行輸入n個整數表示數組;

接下來m行,每行輸入一個字符ch和兩個整數x,y。ch='F'表示查詢x到y這段閉區間的和;ch='S'表示第x個元素加上y。

輸出格式

對于每個查詢,輸出結果。

樣例輸入

5 6
1 2 3 4 5
F 1 3
S 1 2
F 1 3
S 2 3
F 1 2
F 1 5

樣例輸出

6
8
8
20

完整代碼實現如下:

#include <iostream>using namespace std;const int MAX = 1e6;
int c[MAX]; // c[i]表示從第i個元素向前數lowbit(i)個元素,這一段的和,包括c[i]int lowbit(int x)
{return x & -x;
}/*** @brief 求下標為x的前綴和** @param x* @return int*/
int sum(int x, int c[])
{int res = 0;for (; x > 0; x -= lowbit(x))res += c[x];return res;
}/*** @brief 原數組x的位置上的數加上了val,所以要維護c數組** @param x* @param val* @param c* @param n*/
void update(int x, int val, int c[], int n)
{for (; x <= n; x += lowbit(x))c[x] += val;
}int main()
{int n, m;cin >> n >> m;for (int i = 1; i <= n; i++){int x;cin >> x;update(i, x, c, n);}while (m--){int x, y;char ch;cin >> ch >> x >> y;switch (ch){case 'F':cout << sum(y, c) - sum(x - 1, c) << endl;break;case 'S':update(x, y, c, n);break;}}return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/62914.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/62914.shtml
英文地址,請注明出處:http://en.pswp.cn/web/62914.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

freeswitch(開啟支持MCU視頻會議,使用mod_av模塊)

親測版本centos 7.9系統–》 freeswitch1.10.9 本人freeswitch安裝路徑(根據自己的路徑進入) /usr/local/freeswitch/etc/freeswitch場景說明: 有些場景想使用視頻會議MCU融合畫面進行開會使用方法: 第一步:下載插件 yum install -y epel-release yum install

【大數據技術基礎】【記錄Ubuntu 16.04升級到18.04】Ubuntu的一個版本升級到另一個版本

在 Ubuntu 操作系統中進行軟件更新和系統升級 Ubuntu Kylin 16.04 LTS 系統進行系統升級到 Ubuntu 18.04.6 LTS 版本 升級提示&#xff1a;系統彈出提示框&#xff0c;告知用戶有新版本的 Ubuntu 可用&#xff0c;詢問用戶是否想要升級。 認證窗口&#xff1a;顯示了一個認證…

這是一個vue3 + scss的數字滾動效果

介紹: 當數字變化時&#xff0c;只改變變化的數字位&#xff0c;其余的不變&#xff0c;可以遞增、遞減、驟變、負數也可以&#xff0c;但是樣式要根據具體的項目需求去改&#xff1b; 效果1、增加數字&#xff1a; 效果2、減少數字&#xff1a; 使用方法&#xff1a; <te…

TortoiseGit的下載、安裝和配置

一、TortoiseGit的簡介 tortoiseGit是一個開放的git版本控制系統的源客戶端&#xff0c;支持Winxp/vista/win7.該軟件功能和git一樣 不同的是&#xff1a;git是命令行操作模式&#xff0c;tortoiseGit界面化操作模式&#xff0c;不用記git相關命令就可以直接操作&#xff0c;讀…

最新版Chrome瀏覽器加載ActiveX控件之Adobe PDF閱讀器控件

背景 Adobe PDF閱讀器控件是一個ActiveX控件&#xff0c;用于在Windows平臺上顯示和操作PDF文件。它提供了一系列方法和屬性&#xff0c;可以實現對PDF文件的加載、顯示、搜索、打印、保存等操作。 allWebPlugin中間件是一款為用戶提供安全、可靠、便捷的瀏覽器插件服務的中間件…

linux在沒網的情況下如何校驗時間 超詳細拿來即用

一、沒有校時服務器的話 1、手動修改 sudo date --set"2024-06-17 13:44:00"二、有校時服務器的話 1、手動校時 ntpdate 14.193.73.22、自動校時 寫一個校時服務腳本 14.193.73.2 是校驗時間服務器 #!/bin/sh while true dontpdate 14.193.73.2sleep 5;hwclock…

源碼分析之Openlayers中的控件篇Control基類介紹

概述 Openlayers 中內置了9類控件&#xff0c;這9類控件都是基于Control類&#xff0c;而Control類則是繼承于BaseObject類&#xff0c;如下圖所示&#xff1a; 如上&#xff0c;這9類控件分別是&#xff1a; Attribution&#xff1a;屬性控件FullScreen:全屏控件MousePositi…

計算機網絡知識點全梳理(二.HTTP知識點總結)

目錄 HTTP基本概念 HTTP優缺點 HTTP優點&#xff08;1.1&#xff09; HTTP缺點 HTTP與HTTPS HTTP 與 HTTPS 的區別 HTTPS 解決 HTTP 的哪些安全問題&#xff1f; HTTPS 如何解決安全問題&#xff1f; HTTPS 連接建立的過程&#xff1a; HTTP/1.1、HTTP/2、HTTP/3 演…

第P2周:Pytorch實現CIFAR10彩色圖片識別

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 目標 實現CIFAR-10的彩色圖片識別實現比P1周更復雜一點的CNN網絡 具體實現 &#xff08;一&#xff09;環境 語言環境&#xff1a;Python 3.10 編 譯 器: …

Quant connect的優勢和不足,學習曲線難

Quant connect的優勢和不足 Quant connect作為一個成熟的算法交易平臺&#xff0c;具有許多優勢&#xff0c;包括&#xff1a; 強大的回測功能&#xff1a;Quant connect提供了豐富的數據源和回測功能&#xff0c;可以對各種交易策略進行全面的回測和分析。 容易上手&#xf…

深入理解 Ansible Playbook:組件與實戰

目錄 1 playbook介紹 2 YAML語言 2.1語法簡介 2.2數據類型 3 Playbook核心組件 3.1 hosts組件 3.2 remote_user組件 3.3 task列表和action組件 3.4 handlers 3.5 tags組件 3.6 其他組件說明 1 playbook介紹 playbook 劇本是由一個或多個"play"組成的列表。…

2024年食堂采購系統源碼技術趨勢:如何開發智能的供應鏈管理APP

本篇文章&#xff0c;小編將與大家一同探討2024年食堂采購系統的技術趨勢&#xff0c;并提供開發更智能的供應鏈管理APP的策略。 一、2024年食堂采購系統的技術趨勢 1.人工智能與機器學習的深度應用 在2024年&#xff0c;AI和機器學習在食堂采購系統中的應用將更加普遍。這些…

代碼隨想錄-算法訓練營-番外(圖論01:圖論理論基礎,所有可到達的路徑)

day01 圖論part01 今日任務:圖論理論基礎/所有可到達的路徑 代碼隨想錄圖論視頻部分還沒更新 https://programmercarl.com/kamacoder/圖論理論基礎.html#圖的基本概念 day01 所有可達路徑 鄰接矩陣 import java.util.Scanner;import java.util.List;import java.util.ArrayL…

系統架構的演變

什么是系統架構&#xff1f; 系統架構是系統的一種整體的高層次的結構表示&#xff0c;它確定了系統的基本組織、組件之間的關系、組件與環境的關系&#xff0c;以及指導其設計和發展的原則。隨著技術的發展和業務需求的增長&#xff0c;系統架構經歷了從簡單到復雜、從集中到…

c++總復習

C 中多態性在實際項目中的應用場景 圖形繪制系統 描述&#xff1a;在一個圖形繪制軟件中&#xff0c;可能有多種圖形&#xff0c;如圓形、矩形、三角形等。這些圖形都有一個共同的操作&#xff0c;比如繪制&#xff08;draw&#xff09;。通過多態性&#xff0c;可以定義一個基…

pip離線安裝一個github倉庫

要使用pip安裝一個本地Git倉庫&#xff0c;你可以按照以下步驟操作&#xff1a; 確保你已經克隆了Git倉庫到本地。 進入倉庫所在的目錄。 使用pip安裝。 以下是具體的命令&#xff1a; 克隆Git倉庫到本地&#xff08;替換下面的URL為你的倉庫URL&#xff09; git clone https…

【從零開始入門unity游戲開發之——C#篇04】棧(Stack)和堆(Heap),值類型和引用類型,以及特殊的引用類型string

文章目錄 知識回顧一、棧&#xff08;Stack&#xff09;和堆&#xff08;Heap&#xff09;1、什么是棧和堆2、為什么要分棧和堆3、棧和堆的區別棧堆 4、總結 二、值類型和引用類型1、那么值類型和引用類型到底有什么區別呢&#xff1f;值類型引用類型 2、總結 三、特殊的引用類…

【C語言實現:用隊列模擬棧與用棧模擬隊列(LeetCode 225 232)】

LeetCode刷題記錄 &#x1f310; 我的博客主頁&#xff1a;iiiiiankor&#x1f3af; 如果你覺得我的內容對你有幫助&#xff0c;不妨點個贊&#x1f44d;、留個評論?&#xff0c;或者收藏?&#xff0c;讓我們一起進步&#xff01;&#x1f4dd; 專欄系列&#xff1a;LeetCode…

【Python】Selenium 爬蟲的使用技巧和案例

引言 Selenium 是 Python 中功能強大的自動化測試工具,因其能夠操控瀏覽器進行模擬操作,被廣泛應用于網頁數據爬取。相比傳統的 requests 等庫,Selenium 能更好地應對動態加載內容和復雜交互場景。本文將詳細介紹 Selenium 爬蟲的使用技巧,并提供實際案例來幫助讀者快速上…

MySQL SQL語句性能優化

MySQL SQL語句性能優化指南 一、查詢設計優化1. 避免 SELECT *2. 使用 WHERE 進行條件過濾3. 避免在索引列上使用函數和表達式4. 使用 LIMIT 限制返回行數5. 避免使用子查詢6. 優化 JOIN 操作7. 避免全表掃描 二、索引優化1. 使用合適的索引2. 覆蓋索引3. 索引選擇性4. 多列索引…