劃分樹講解

2021-08-03 18:18:33 字數 2880 閱讀 5108

搜尋關於劃分樹的講解,竟然搜到了鄭大媛學姐的博文,不是這篇

原博文有錯誤,我就不給出原鏈結了

用劃分樹來解決選定區間內的第k大值,其實也就兩步!一步是建樹,另一步則是查詢。

先說我對建樹的理解吧!

建樹的過程很簡單:兩步就ok了!

第一步:找到序列的中位數,把大於中位數的扔到中位數的左邊,小於中位數的扔到數的右邊。這樣整個序列就被分成了兩個區間。

第二步:對每個子區間,也分別執行第一步操作,直到序列中只有乙個元素為止。

可以看出,建樹是乙個遞迴的過程,與線段樹的建樹有相似之處。

劃分樹的建樹需要注意以下幾點:

第一:建樹是分層的,所以**中用的是二維陣列tree[20][m]。一般10w級別的資料,20層已經夠了。

第二:建樹劃分的標準是中位數,所以需要排序。而且只排一次序就ok了,為什麼只排一次就ok了,我很久都沒明白這一點。其實是這樣的:對於任意序列: 劃分後,左邊的資料永遠不會大於右邊的資料。那麼對左邊資料單獨排序與整體排序的結果是一樣的,所以排一次序就ok了!

第三:劃分樹劃分好的資料永遠在存放在下一層。比如資料:

tree[0][m]=1 5 2 6 3 7 4

排序後為:1 2 3 4 5 6 7

中位數為:4

劃分後的結果為:tree[1][m]=1 2 3 4 5 6 7(這組資料有點特殊,劃分後來就已經是排好序的了)紅黑色字型都仍按原未排順序排列

(紅色表示劃分到中位數的左邊,黑色表示劃分到中位數的右邊)

接著劃分:tree[2][m]=1 2 3 4 5 6 7

再接著分:tree[3][m]=1 2 3 4 5 6 0

到這裡已經分完了,為什麼最後是0呢?在第2層(tree[2][m]),7已經分完了,所以不用再分

第四:劃分到最後,實際上已經對序列進行排序了。

劃分的時候還有一點需要處理:如果有多個資料相同怎麼辦呢?通過一種特殊的處理:盡量使左右兩邊平均分配相同的數。這個特殊處理是這樣的:

在沒分之前,先假設中位數左邊的資料suppose都已經分到左邊了,所以suppose=mid-left+1;然後如果真的分在左邊,即if(tree[level][i]

suppose--;suppose就減一!到最後,如果suppos=1,則說明中位數左邊的數都小於中位數,如果有等於中位數的,則suppose大於1。

最後分配的時候,把suppose個數,分到左邊就可以了,剩下的分到右邊!因為suppose的初值是mid-left+1,這樣就能保證中位數左邊和右邊的數平衡了!

第五:劃分的過程,需要把每層的資料記錄:toleft[20][m]。toleft[i][j]定義為:第i層[l,j]之間有多個資料被分到了左邊(注意這裡用的是閉區間)

模板:[cpp]view plain

copy

print?

#include

#include

using

namespace

std;    

#define m 100005  

inttree[20][m],sorted[m];    

inttoleft[20][m];    

void

build(

intlevel,

intleft,

intright)    

}    

/*如果suppose==1,則說明陣列中值為sorted[mid]只有乙個數。

比如序列:1 3 4 5 6,sorted[mid]=4  

*//*如果suppose>1,則說明陣列中左半邊值為sorted[mid]的不止乙個數,為mid-suppose。

比如序列:1 4 4 4 6,sorted[mid]=4 

*/int

lpos=left,rpos=mid+1;    

for(i=left;i<=right;i++)  

else

if(tree[level][i]

//劃分到中位數左邊  

toleft[level][i]++;    

tree[level+1][lpos++]=tree[level][i];    

}  else

if(tree[level][i]>sorted[mid])  

else

else

}    

}    

build(level+1,left,mid);    

build(level+1,mid+1,right);    

}    

//在[left,right]資料中查詢[qleft,qright]中第k大的資料  

intquery(

intlevel,

intleft,

intright,

intqleft,

intqright,

intk)else

intnewl,newr;    

if(k<=ss)  

else

}    

intmain()    

sort(sorted+1,sorted+n+1);    

build(0,1,n);    

intql,qr,k;    

for(i=0;i

scanf("%d %d %d"

,&ql,&qr,&k);    

printf("%d\n"

,query(0,1,n,ql,qr,k));    

}    

}    

return

0;    

}  

劃分樹小結

最近學習了一下劃分樹,下面總結一下。我們在求區間最值的時候,一般可以用線段樹解決,但是如果要求區間第k小或者第k大值的話線段樹就有點力不從心了,這是我們可以用劃分樹來解決。劃分樹利用了快速排序的思想,首先是建樹,我們設當前區間的中位數為mid,為了能快速找到區間的中位數,我們一般先對原序列做一次排序...

劃分樹詳解

題目 給出n個數,和m個區間 對於每個區間 l r 輸出這個區間裡面第k大的數。我們來看看劃分樹是怎麼構造的。構造 以2104為例子,舉2 0 6 8 5 1 4 3 9為例子,構造劃分樹是這樣的 上面的內容是用乙個二位陣列val儲存下來的,第一維表示劃分樹的第幾層,第二位表示在這一層中的位置。要注...

劃分樹模板

border 0 width 330 height 86 src upd 3.17 複習了一些劃分樹 做了一下簡單的複習筆記 傳到雲上了qaq 恩 打了半小時 恩 213的把瀏覽器關掉了 恩 心好累 不打劃分樹的詳細解釋了 粘三份 好了 第乙份是t的 蒟蒻捂臉 的我 沒學劃分樹的最好看一下 對理解後...