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