日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

搞懂树状数组

發(fā)布時(shí)間:2023/12/18 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 搞懂树状数组 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

引用請注明出處:http://blog.csdn.net/int64ago/article/details/7429868

????? 寫下這個(gè)標(biāo)題,其實(shí)心里還是沒底的,與其說是寫博帖,不如說是做總結(jié)。第一個(gè)接觸樹狀數(shù)組還是兩年前,用什么語言來形容當(dāng)時(shí)的感覺呢?……太神奇了!真的,無法表達(dá)出那種感覺,她是那么的優(yōu)雅,10行不到的代碼,卻把事情干的如此出色!沒有了解她原理的前提下即使把代碼倒背如流也理解不了!其中,我就是一直沒搞懂地在使用她。時(shí)隔兩年,又無意遇到了她,可能是兩年的代碼經(jīng)驗(yàn)的積累,有了些新的認(rèn)識,可以自信的說理解了吧!下面我爭取用自己的方式讓更多人明白她,而不是背誦她。為了更方便的說明,文章里會(huì)自己強(qiáng)加一些概念,只是為了更好的理解,不是什么專業(yè)術(shù)語之類的。

一、樹狀數(shù)組是干什么的?

?????? 平常我們會(huì)遇到一些對數(shù)組進(jìn)行維護(hù)查詢的操作,比較常見的如,修改某點(diǎn)的值、求某個(gè)區(qū)間的和,而這兩種恰恰是樹狀數(shù)組的強(qiáng)項(xiàng)!當(dāng)然,數(shù)據(jù)規(guī)模不大的時(shí)候,對于修改某點(diǎn)的值是非常容易的,復(fù)雜度是O(1),但是對于求一個(gè)區(qū)間的和就要掃一遍了,復(fù)雜度是O(N),如果實(shí)時(shí)的對數(shù)組進(jìn)行M次修改或求和,最壞的情況下復(fù)雜度是O(M*N),當(dāng)規(guī)模增大后這是劃不來的!而樹狀數(shù)組干同樣的事復(fù)雜度卻是O(M*lgN),別小看這個(gè)lg,很大的數(shù)一lg就很小了,這個(gè)學(xué)過數(shù)學(xué)的都知道吧,不需要我說了。申明一下,看下面的文章一定不要急,只需要看懂每一步最后自然就懂了。

二、樹狀數(shù)組怎么干的?

??????? 先看兩幅圖(網(wǎng)上找的,如果雷同,不要大驚小怪~),下面的說明都是基于這兩幅圖的,左邊的叫A圖吧,右邊的叫B圖:

????? 是不是很像一顆樹?對,這就是為什么叫樹狀數(shù)組了~先看A圖,a數(shù)組就是我們要維護(hù)和查詢的數(shù)組,但是其實(shí)我們整個(gè)過程中根本用不到a數(shù)組,你可以把它當(dāng)作一個(gè)擺設(shè)!c數(shù)組才是我們?nèi)剃P(guān)心和操縱的重心。先由圖來看看c數(shù)組的規(guī)則,其中c8 = c4+c6+c7+a8,c6 = c5+a6……先不必糾結(jié)怎么做到的,我們只要知道c數(shù)組的大致規(guī)則即可,很容易知道c8表示a1~a8的和,但是c6卻是表示a5~a6的和,為什么會(huì)產(chǎn)生這樣的區(qū)別的呢?或者說發(fā)明她的人為什么這樣區(qū)別對待呢?答案是,這樣會(huì)使操作更簡單!看到這相信有些人就有些感覺了,為什么復(fù)雜度被lg了呢?可以看到,c8可以看作a1~a8的左半邊和+右半邊和,而其中左半邊和是確定的c4,右半邊其實(shí)也是同樣的規(guī)則把a(bǔ)5~a8一分為二……繼續(xù)下去都是一分為二直到不能分,可以看看B圖。怎么樣?是不是有點(diǎn)二分的味道了?對,說白了樹狀數(shù)組就是巧妙的利用了二分,她并不神秘,關(guān)鍵是她的巧妙!

?????? 她又是怎樣做到不斷的一分為二呢?說這個(gè)之前我先說個(gè)叫l(wèi)owbit的東西,lowbit(k)就是把k的二進(jìn)制的高位1全部清空,只留下最低位的1,比如10的二進(jìn)制是1010,則lowbit(k)=lowbit(1010)=0010(2進(jìn)制),介于這個(gè)lowbit在下面會(huì)經(jīng)常用到,這里給一個(gè)非常方便的實(shí)現(xiàn)方式,比較普遍的方法lowbit(k)=k&-k,這是位運(yùn)算,我們知道一個(gè)數(shù)加一個(gè)負(fù)號是把這個(gè)數(shù)的二進(jìn)制取反+1,如-10的二進(jìn)制就是-1010=0101+1=0110,然后用1010&0110,答案就是0010了!明白了求解lowbit的方法就可以了,繼續(xù)下面。介于下面討論十進(jìn)制已經(jīng)沒有意義(這個(gè)世界本來就是二進(jìn)制的,人非要主觀的構(gòu)建一個(gè)十進(jìn)制),下面所有的數(shù)沒有特別說明都當(dāng)作二進(jìn)制。

?????? 上面那么多文字說lowbit,還沒說它的用處呢,它就是為了聯(lián)系a數(shù)組和c數(shù)組的!ck表示從ak開始往左連續(xù)求lowbit(k)個(gè)數(shù)的和,比如c[0110]=a[0110]+a[0101],就是從110開始計(jì)算了0010個(gè)數(shù)的和,因?yàn)閘owbit(0110)=0010,可以看到其實(shí)只有低位的1起作用,因?yàn)楹茱@然可以寫出c[0010]=a[0010]+a[0001],這就為什么我們?nèi)魏螖?shù)都只關(guān)心它的lowbit,因?yàn)楦呶徊黄鹱饔?#xff08;基于我們的二分規(guī)則它必須如此!),除非除了高位其余位都是0,這時(shí)本身就是lowbit。

既然關(guān)系建立好了,看看如何實(shí)現(xiàn)a某一個(gè)位置數(shù)據(jù)跟改的,她不會(huì)直接改的(開始就說了,a根本不存在),她每次改其實(shí)都要維護(hù)c數(shù)組應(yīng)有的性質(zhì),因?yàn)楹竺媲蠛鸵玫健6S護(hù)也很簡單,比如更改了a[0011],我們接著要修改c[0011],c[0100],c[1000],這是很容易從圖上看出來的,但是你可能會(huì)問,他們之間有申明必然聯(lián)系嗎?每次求解總不能總要拿圖來看吧?其實(shí)從0011——>0100——>1000的變化都是進(jìn)行“去尾”操作,又是自己造的詞--'',我來解釋下,就是把尾部應(yīng)該去掉的1都去掉轉(zhuǎn)而換到更高位的1,記住每次變換都要有一個(gè)高位的1產(chǎn)生,所以0100是不能變換到0101的,因?yàn)闆]有新的高位1產(chǎn)生,這個(gè)變換過程恰好是可以借助我們的lowbit進(jìn)行的,k +=lowbit(k)。

?????? 好吧,現(xiàn)在更新的次序都有了,可能又會(huì)產(chǎn)生新的疑問了:為什么它非要是這種關(guān)系啊?這就要追究到之前我們說c8可以看作a1~a8的左半邊和+右半邊和……的內(nèi)容了,為什么c[0011]會(huì)影響到c[0100]而不會(huì)影響到c[0101],這就是之前說的c[0100]的求解實(shí)際上是這樣分段的區(qū)間 c[0001]~c[0001] 和區(qū)間c[0011]~c[0011]的和,數(shù)字太小,可能這樣不太理解,在比如c[0100]會(huì)影響c[1000],為什么呢?因?yàn)閏[1000]可以看作0001~0100的和加上0101~1000的和,但是0101位置的數(shù)變化并會(huì)直接作用于c[1000],因?yàn)樗奈膊?不能一下在跳兩級在產(chǎn)生兩次高位1,是通過c[0110]間接影響的,但是,c[0100]卻可以跳一級產(chǎn)生一次高位1。

???????? 可能上面說的你比較繞了,那么此時(shí)你只需注意:c的構(gòu)成性質(zhì)(其實(shí)是分組性質(zhì))決定了c[0011]只會(huì)直接影響c[0100],而c[0100]只會(huì)直接影響[1000],而下表之間的關(guān)系恰好是也必須是k +=lowbit(k)。此時(shí)我們就是寫出跟新維護(hù)樹的代碼:

void add(int k,int num) {while(k<=n){tree[k]+=num;k+=k&-k;} } ?????? 有了上面的基礎(chǔ),說求和就比較簡單了。比如求0001~0110的和就直接c[0100]+c[0110],分析方法與上面的恰好逆過來,而且寫法也是逆過來的,具體就不累述了:

int read(int k)//1~k的區(qū)間和 {int sum=0;while(k){sum+=tree[k];k-=k&-k;}return sum; }

三、總結(jié)一下吧

????????? 首先,明白樹狀數(shù)組所白了是按照二分對數(shù)組進(jìn)行分組;維護(hù)和查詢都是O(lgn)的復(fù)雜度,復(fù)雜度取決于最壞的情況,也是O(lgn);lowbit這里只是一個(gè)技巧,關(guān)鍵在于明白c數(shù)組的構(gòu)成規(guī)律;分析的過程二進(jìn)制一定要深入人心,當(dāng)作心目中的十進(jìn)制。


總結(jié)

以上是生活随笔為你收集整理的搞懂树状数组的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。