樹狀數(shù)組[要點]

Advise Category: Algorithm >> 樹狀數(shù)組

Scenario

  1. 單點更新
  2. 區(qū)間求和(前綴和)

Objects

  1. 待處理數(shù)組兆龙,a[1...n]
  2. 待維護樹狀數(shù)組逼纸,c[1...n]
  3. 結(jié)果數(shù)組(前綴和)(待處理數(shù)組的前綴和)缀去,r[1...n]

Ideas

Q:

r[n] = a[1] + ... + a[n]

A1:

每次計算r[i]都遍歷a的前i項干旁,一個結(jié)果r[i]一次遍歷撒桨,n個結(jié)果r[n]需要n次遍歷(不適合大數(shù)據(jù)數(shù)組的情況)董虱。

A2:(樹狀)
孽椰?如何加速遍歷

不與a[i]為維度進行運算鹃愤,而是通過維護一個數(shù)組(數(shù)組)c[1...n]其中每一項是通過一定規(guī)律的m項和搓彻,再通過規(guī)律找到前綴和r[i]的待累積c[i]

并求和如绸。

Method for A2

a[i] -> c[i]:
c[1] = c[0001] = a[1];
c[2] = c[0010] = a[1]+a[2];
c[3] = c[0011] = a[3];
c[4] = c[0100] = a[1]+a[2]+a[3]+a[4];
c[5] = c[0101] = a[5];
c[6] = c[0110] = a[5]+a[6];
c[7] = c[0111] = a[7];
c[8] = c[1000] = a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8];
......
樹狀.png
Rule:
c[i]=a[i-2^k+1]+a[i-2^k+2]+......a[i];

Note:

  • ki的二進制中從最低位到高位連續(xù)零的長度, 例如i=8(1000)時,k=3旭贬。
  • 可以理解為這是一種分類方法怔接,通過維護分類(要么有零,要么沒零稀轨,有零說明進位了需要把其下的數(shù)都考慮在內(nèi))數(shù)組c[i]讓前綴求和變得更快扼脐。

單點更新

?如果修改a中的一個元素奋刽,c中的元素如何變化

Assumption:a[3] = a[3] + 1瓦侮,從3往后找,直到數(shù)組結(jié)束佣谐。

lowbit(3)=0001=2^0 3+lowbit(3)=04(00100)    c[04] += 1
lowbit(4)=0100=2^2 4+lowbit(4)=08(01000)    c[08] += 1
lowbit(8)=1000=2^3 8+lowbit(8)=16(10000)    c[16] += 1
......

Note:

  • 可以看出a[3]變化之后肚吏,會涉及到c[4]/c[8]/[16]...的變化,所以需要更新跟隨變化的c中的元素狭魂。
罚攀?lowbit

lowbit(x)是取出x的最低位1(從右往左數(shù)第一個1)党觅,滿足:

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

Note:

  • 一個數(shù)的負數(shù)就等于對這個數(shù)取反+1

  • 補碼和原碼必然相反,所以原碼有0的部位補碼全是1,補碼再+1之后由于進位那么最末尾的1和原碼最右邊的1一定是同一個位置

  • 剛好等于2^kkx的二進制中從最低位到高位連續(xù)零的長度

Code:

void update(int x,int y,int n){
    for(int i=x; i <= n; i += lowbit(i))    //x為更新的位置,y為更新后的數(shù),n為數(shù)組最大值
        c[i] += y;
}

區(qū)間求和

e.g 求r[5]

Disappear:

c[4]=a[1]+a[2]+a[3]+a[4];
c[5]=a[5];

sum(i = 5) = c[4] + c[5];
sum(i = 101) = c[(100)] + c[(101)];

Note:

  • 首次從101,減去最低位的1就是100斋泄,剛好是單點更新的你操作杯瞻。

Code:

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

Example

leet-code:

計算右側(cè)小于當(dāng)前元素的個數(shù)

Ans:

class Solution{
   public List<Integer> countSmaller3(int[] nums){
        if(nums == null) return null;
        if(nums.length == 0) return new ArrayList<>();

        List<Integer> result = new ArrayList<>();
        Set<Integer> set = new HashSet<Integer>();
        for (int i = 0; i < nums.length; i++) {
            set.add(nums[i]);
        }

        int[] c = Arrays.stream(set.toArray()).sorted().mapToInt(e -> Integer.parseInt(e.toString())).toArray();
        int[] d = new int[c.length + 2];
        for (int i = nums.length - 1; i > -1; i--) {
            int idx = Arrays.binarySearch(c, nums[i]) + 1;
            int s = sum(d, idx - 1);
            update(d, idx, 1);
            result.add(s);
        }

        Collections.reverse(result);
        return result;
    }

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

    private void update(int[] c, int i, int delta){
        while( i <= c.length - 1 ){
            c[i] += delta;
            i += lowbit(i);
        }
    }

    private int sum(int[] c, int i){
        int ans = 0;
        while( i > 0 ){
            ans += c[i];
            i -= lowbit(i);
        }

        return ans;
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市炫掐,隨后出現(xiàn)的幾起案子魁莉,更是在濱河造成了極大的恐慌,老刑警劉巖募胃,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旗唁,死亡現(xiàn)場離奇詭異,居然都是意外死亡摔认,警方通過查閱死者的電腦和手機逆皮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來参袱,“玉大人电谣,你說我怎么就攤上這事∧ㄊ矗” “怎么了剿牺?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長环壤。 經(jīng)常有香客問我晒来,道長,這世上最難降的妖魔是什么郑现? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任湃崩,我火速辦了婚禮,結(jié)果婚禮上接箫,老公的妹妹穿的比我還像新娘攒读。我一直安慰自己,他們只是感情好辛友,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布薄扁。 她就那樣靜靜地躺著,像睡著了一般废累。 火紅的嫁衣襯著肌膚如雪邓梅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天邑滨,我揣著相機與錄音日缨,去河邊找鬼。 笑死掖看,一個胖子當(dāng)著我的面吹牛殿遂,可吹牛的內(nèi)容都是我干的诈铛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼墨礁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了耳峦?” 一聲冷哼從身側(cè)響起恩静,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蹲坷,沒想到半個月后驶乾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡循签,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年级乐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片县匠。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡风科,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出乞旦,到底是詐尸還是另有隱情贼穆,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布兰粉,位于F島的核電站故痊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏玖姑。R本人自食惡果不足惜愕秫,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望焰络。 院中可真熱鬧戴甩,春花似錦、人聲如沸舔琅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽备蚓。三九已至课蔬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間郊尝,已是汗流浹背二跋。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留流昏,地道東北人扎即。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓吞获,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谚鄙。 傳聞我的和親對象是個殘疾皇子各拷,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354