js 数组 实现 完全树_算法和数据结构 | 树状数组(Binary Indexed Tree)
本文來源于力扣圈子,作者:胡小旭。點(diǎn)擊查看原文
力扣?leetcode-cn.com樹狀數(shù)組或二叉索引樹(英語:Binary Indexed Tree),又以其發(fā)明者命名為 Fenwick 樹。其初衷是解決數(shù)據(jù)壓縮里的累積頻率(Cumulative Frequency)的計算問題,現(xiàn)多用于高效計算數(shù)列的前綴和, 區(qū)間和。它可以以
的時間得到任意前綴和,并同時支持在 時間內(nèi)支持動態(tài)單點(diǎn)值的修改。空間復(fù)雜度 。文章先介紹低位運(yùn)算(lowbit)的基本知識,再提及如何將一個整數(shù)劃分為
個區(qū)間的運(yùn)算過程,進(jìn)而延展到如何將線性序列以樹行結(jié)構(gòu)進(jìn)行存取,接著介紹了高級數(shù)據(jù)結(jié)構(gòu)——樹狀數(shù)組的兩個基本操作——查詢前綴和與單點(diǎn)增加,最后介紹了樹狀數(shù)組的一個應(yīng)用——求解逆序?qū)?shù)。lowbit(低位)運(yùn)算
定義為非負(fù)整數(shù) 在二進(jìn)制表示下 “最低位的 1 及其后邊所有的 0” 構(gòu)成的數(shù)值。比如:
,其二進(jìn)制表示為 ,則其低位公式
如何計算一個整數(shù)
中二進(jìn)制表示下所有位是 1 的數(shù)值?比如
,則其二進(jìn)制表示下所有位是 1 的數(shù)值有: , 。樸素算法需要枚舉整數(shù)中所有的位,時間復(fù)雜度為
, 為整數(shù) 的二進(jìn)制表示下的位數(shù)。為了高效獲取二進(jìn)制表示下所有位是 1 的數(shù)值,可以利用
運(yùn)算,得到時間復(fù)雜度 , 為二進(jìn)制表示下為 1 的位的個數(shù)。比如
, ;接著令 ,則 ;接著令 ,停止。為了得到
的第幾位為 1,可以對 2 和 8 分別取對數(shù),即 , 。由于 C++ math.h 庫的 函數(shù)是以 為底的實(shí)數(shù)運(yùn)算,并且復(fù)雜度常數(shù)較大,所以可以通過預(yù)處理,利用哈希表來代替 運(yùn)算。代碼
C++ 實(shí)現(xiàn)
const MAX_N = 1 << 20; int H[MAX_N + 1]; for (int i = 0; i <= 20; ++i) H[1 << i] = i; while (cin >> n) {while (n > 0) {cout << H[n & -n] << ' ';n -= n & -n;}cout << endl; }樹狀數(shù)組
假設(shè)整數(shù)
,其二進(jìn)制表示形式為: 代表二進(jìn)制表示下位為 1 的索引下標(biāo)值,且假設(shè) 。那么,可以將區(qū)間 [1,n] 劃分成
個小區(qū)間。- ...
比如,
,那么 區(qū)間可以劃分成 , 和 ,其區(qū)間長度分別為 , 和 。利用
運(yùn)算計算區(qū)間:C++ 實(shí)現(xiàn)
while (x > 0) {printf("[%d, %d]n" x - lowbit(x) + 1, x);x -= lowbit(x); }樹狀數(shù)組是基于以上思想的數(shù)據(jù)結(jié)構(gòu),基本用途是維護(hù)序列的前綴和。
那么,假設(shè)有序列
,現(xiàn)在的問題就是如何將這個序列劃分成 個小區(qū)間。不妨,利用序列的索引值(以 1 為起點(diǎn)開始計數(shù)),根據(jù)上述計算區(qū)間的方式,將其以如下樹形結(jié)構(gòu)展開。樹狀數(shù)組(Binary Indexed Tree) 以樹形結(jié)構(gòu)展開的序列 A此時,以樹形結(jié)構(gòu)展開的序列 A 中的每一個節(jié)點(diǎn)都對應(yīng)著樹狀數(shù)組中的一個值。那么這個值為以當(dāng)前節(jié)點(diǎn)為根的子樹中所有節(jié)點(diǎn)值的總和。
接著,我們看下以樹形結(jié)構(gòu)展開的樹狀數(shù)組是什么樣的。
以樹形結(jié)構(gòu)展開的樹狀數(shù)組(Binary Indexed Tree)- Index 代表序列 A 中元素的索引,為了方便,以 1 為起點(diǎn)計數(shù)
- Original Value 代表序列 A 中的元素值
- BIT Value(Binary Indexed Tree Value)代表樹狀數(shù)組中的值
- Binary bit 代表索引值的二進(jìn)制形式
- Low bit 代表索引值的二進(jìn)制形式下的地位
上圖中最大的區(qū)別是某些節(jié)點(diǎn)中的值發(fā)生了變化。這是因為,在以樹形結(jié)構(gòu)展開的樹狀數(shù)組中的每一個值代表的是一個區(qū)間的總和。這個區(qū)間即為我們上述求解的區(qū)間,比如一個整數(shù) 7,可以將其劃分成
, 和 三個小區(qū)間。那么,這三個小區(qū)間的右端值作為索引對應(yīng)的樹狀數(shù)組中的值即為當(dāng)前區(qū)間元素的總和。比如
對應(yīng)的樹狀數(shù)組的值為(BIT Value)10,它代表 這個區(qū)間的和。再比如
對應(yīng)的樹狀數(shù)組的值為 11,它代表 這個區(qū)間的和。基本操作
樹狀數(shù)組支持兩個基本操作——查詢前綴和,單點(diǎn)增加。
查詢前綴和
在尋求序列 A 的前 n 項的前綴和時,等于
代表的 個區(qū)間的總和。C++ 實(shí)現(xiàn)
int query(int x) {int ans = 0;for (; x; x -= x & -x) ans += bit[x];return ans; }單點(diǎn)增加
觀察父子節(jié)點(diǎn)的關(guān)系,可以推算出,父節(jié)點(diǎn)的索引 parent(i),為其子節(jié)點(diǎn)索引值 + 其低位——
.C++ 實(shí)現(xiàn)
void update(int x, int delta) {for (; x; x += x & -x) bit[x] += delta; }關(guān)于查詢前綴和與單點(diǎn)增加的計算過程,可以觀看下面視頻展示的動畫。
樹狀數(shù)組與逆序?qū)?/h2>
對于一個序列
,如果 ,并且 ,那么則稱 與 構(gòu)成逆序?qū)Α@脴錉顢?shù)組數(shù)據(jù)結(jié)構(gòu)可以求解序列 中的逆序?qū)€數(shù)。C++ 實(shí)現(xiàn)
int cnt = 0; for (int i = A.size() - 1; i >= 0; --i) {cnt += query(A[i]);update(A[i], 1); }在每一次更新
樹狀數(shù)組時,以元素的值作為樹狀數(shù)組的索引,更新的值為 +1,代表個數(shù)。在每一次獲取
逆序?qū)?shù)時,存在于樹狀數(shù)組中的元素的索引值都比當(dāng)前元素的大(逆序遍歷),那么自然獲取到的樹狀數(shù)組的值即為索引值比當(dāng)前元素的大,且值比當(dāng)前元素的小的個數(shù)。注意,上述的求解過程時,如果序列A的值范圍較大時,那么需要離散化處理。
參考
- 《算法競賽進(jìn)階指南》
- 維基百科——樹狀數(shù)組
本文作者:胡小旭
聲明:本文歸作者版權(quán)所有,如需轉(zhuǎn)載請聯(lián)系。文中圖片和視頻為作者“胡小旭”制作,未經(jīng)允許嚴(yán)禁修改和翻版使用。
總結(jié)
以上是生活随笔為你收集整理的js 数组 实现 完全树_算法和数据结构 | 树状数组(Binary Indexed Tree)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎样保持家里的卫生间气味清新?
- 下一篇: 设置下载安装 桌面_小妖精美化app最新