RMQ問題的樹狀陣列解法

2021-05-27 08:34:27 字數 2711 閱讀 6954

樹狀陣列中每個元素覆蓋了不同長度的子區間,類似於稀疏表(st)演算法的思想,每乙個陣列元素儲存了輸入數列a在該區間的最小值下標。注意:這裡樹狀陣列不是用來儲存區間累加值,而是區間的最小值下標。

這裡針對預處理階段提供兩個演算法:方法1(參考以下實現**中的方法preprocess)採用類似於累加和中的update做法,每乙個元素a[i]需要處理樹狀陣列t中o(logn)個受影響的元素,因此預處理複雜度為o(nlogn)。方法2(參考以下實現**中的方法preprocess2或preprocess3)利用dp(動態規劃)思想,觀察到樹狀陣列中每乙個下標為i的元素覆蓋的區間都可以劃分為:r個子區間 + 最後乙個元素a[i]。(其中2^r=lowbit(i))因此只需要計算這r+1個區間中的最小值即可,該演算法的複雜度為可以這樣計算:對樹中每一層所有元素基本操作做累加,例如葉子結點個數為n/(2^1),每乙個葉子結點基本操作次數為1,倒數第二層的結點個數為n/(2^2),其中每乙個結點的基本操作次數為2....所以總的基本操作次數為1 * n/(2^1) + 2 * n/(2^2) + 3 * n/(2^3) +  ...+ k * n/(2^k) + ... + logn * n/(2^logn) = o(n)。可見第二個方法比第乙個方法複雜度要小。在以下的實現**中,方法preprocess2或preprocess3是等效的,preprocess3使用了類似累加和中的query做法,即找出互不重疊的子區間。

查詢時,採用遞迴做法,分兩種情形: case 1) 如果查詢的區間在當前區間中,則將當前區間最後乙個元素剝離開來,使查詢區間長度減1;case 2)如果查詢的區間與當前區間沒有重疊,則將當前區間所有元素剝離開來,使查詢區間長度減去lowbit值(即2^r)。最糟情況下需要執行case 1)總共logn次,每次查詢時樹高度減1,所以查詢的複雜度為(logn-1) + (logn-2)+...+1=o(logn*logn)。

跟st演算法或線段樹演算法相比,樹狀陣列解rmq問題的優勢是儲存空間小。預處理複雜度跟線段樹演算法相當(採用上面的方法2),但是查詢的複雜度比線段樹演算法中的o(logn)要大。通過時間換空間達到時空平衡,再加上程式設計簡單,樹狀陣列演算法還是有一定價值的。

注意:在下面的**中陣列a的下標永遠都是從0開始,而樹狀陣列t的有效下標從1開始(t[0]沒有被利用)。另外,查詢部分是tail recursion,實際上可以改寫為迭代計算。

實現:/**

* * rmq algorithm using binary indexed tree (bit)

* * licensed under gpl (

* *

* @author ljs

* 2011-08-10

* */

public class rmq_bit

} //preprocess method 1: create bit - o(nlogn)

public void preprocess(int a)

}} }

//another form of preprocess2: similar to query in bit

public void preprocess3(int a)

k -= lowbit(k);

}} }

private int lowbit(int x)

//idx is a's index, starting with 0

private void update(int a,int idx)

x += lowbit(x);

} }//i,j are a's index, starting with 0

public int query(int a,int i,int j)

return query(a,i+1,j+1,j);

} //precondition: p...q must be in the range: 1...n (inclusive)

private int query(int a,int p,int q,int minindex)else }

private void reportlutable(int a){

system.out.format("%n***********************%n");

for(int x=0;x

測試輸出:

rmq for a[0..3]: a[3]=1

***********************

0..[0-9] 2/0 2/0 2/0 1/3 1/3 1/3 1/3 1/3 1/3 1/3

1..[1-9] 4/1 3/2 1/3 1/3 1/3 1/3 1/3 1/3 1/3

2..[2-9] 3/2 1/3 1/3 1/3 1/3 1/3 1/3 1/3

3..[3-9] 1/3 1/3 1/3 1/3 1/3 1/3 1/3

4..[4-9] 6/4 6/4 6/4 6/4 1/8 1/8

5..[5-9] 7/5 7/5 7/5 1/8 1/8

6..[6-9] 8/6 8/6 1/8 1/8

7..[7-9] 9/7 1/8 1/8

8..[8-9] 1/8 1/8

9..[9-9] 7/9

***********************

rmq for a[0..10]: a[5]=5

rmq for a[12..49]: a[14]=7

rmq for a[20..46]: a[23]=12

rmq for a[20..49]: a[47]=8

LCA 樹狀陣列 樹上RMQ

思路 首先求出樹上dfs序列,並且標記樹上每個節點開始遍歷以及最後回溯遍歷到的時間戳,由於需要修改樹上的某兩個節點之間的權值,如果parent v u,那麼說明修改之後的v的子樹到當前根的距離都會改變,由於遍歷到v時有開始時間戳以及結束時間戳,那麼處於這個區間所有節點都會影響到,於是我們可以通過陣列...

LCA 樹狀陣列 樹上RMQ

思路 首先求出樹上dfs序列,並且標記樹上每個節點開始遍歷以及最後回溯遍歷到的時間戳,由於需要修改樹上的某兩個節點之間的權值,如果parent v u,那麼說明修改之後的v的子樹到當前根的距離都會改變,由於遍歷到v時有開始時間戳以及結束時間戳,那麼處於這個區間所有節點都會影響到,於是我們可以通過陣列...

RMQ問題(ST表解法)

針對 範圍最值問題。對於陣列a 1 n 有操作如下 rmq l,r a中第l個數到第r個數中的最小值 最大值模擬 d i j 從i開始,長度為2j 的一段元素的最小值。可推 d i j min d i j 1 d i 1 j 1 j 1 查詢時,令k為滿足2k r l 1的最大整數,由於求最小值可重...