劃分樹(基本用法是求給定區間的第k大的值)

2021-06-25 10:33:39 字數 1513 閱讀 1341

劃分樹,從網上看到的**的風格主要有兩種。

下面的介紹直接是從網上找的看的懂的貼了份過來,其中有些修改。

劃分樹定義為,它的每乙個節點儲存區間[lft,rht]所有元素,元素順序與原陣列(輸入)相同,但是,兩個子樹的元素為該節點所有元素排序後(rht-lft+1)/2個進入左子樹,其餘的到右子樹,同時維護乙個num域,num[i]表示lft->i這個點有多少個進入了左子樹。

如果由下而上看這個圖,我們就會發現它和歸併排序的(歸併樹)的過程很類似,或者說正好相反。歸併樹是由下而上的排序,而它確實是由上而下的排序(觀察』4』的運動軌跡,我們可以猜到,劃分樹的排序也是一種穩定的排序方法,這裡不是說明的重點,不予證明),但這正是它可以用來解決第k大元素的理由所在。(具體的理由,寫完再補)

l  劃分樹的儲存結構(採用層次儲存結構(由下而上,由左到右,每層兩個孩子,見上圖))

constint n=1e5+5;

int sorted[n]; //對原來集合中的元素排序後的值

struct node

t[20];

l  劃分樹的建立build

劃分樹的建立和普通的二叉樹的建立過程差不多,仍然採取中序的過程(先根節點,然後左右孩子)。

樹的建立相對比較簡單,我們依據的是已經排好序的位置進行建樹,所以先用快排將原集合還序。要維護每個節點的num域。

版本一:

void build(int lft,int rht,int ind)

else

tolft[ind][i]=tolft[ind][i-1]+flag;

} build(lft,mid,ind+1);

build(mid+1,rht,ind+1);

}

l  劃分樹的查詢

在區間[a,b]上查詢第k大的元素,同時返回它的位置和區間小於[a,b]的所有數的和。

1.      如果t[p].num[b]-t[p].num[a-1]>=k,即,進入左孩子的個數已經超過k個,那麼就往左孩子裡面查詢,同時更新[a,b]=>[lft+t[p].num[a-1],lft+t[p].num[b]-1]

2.      如果t[p].num[b]-t[p].num[a-1]詳細過程見**和注釋:

/*在區間[a,b]上查詢第k大元素,同時sum返回區間[a,b]中小於第k大元素的和*/

int query(int a,int b,int k,int p,int lft,int rht)

else

if(s>=k) //進入左孩子,同時更新區間端點值。

else

}

版本二:

int query(int st,int ed,int k,int lft,int rht,int ind)

}

靜態區間第k大(劃分樹)

poj 2104為例 經典劃分樹問題 思想 利用快速排序思想,include include using namespace std const int maxn 100010 int tree 20 maxn 每層每個位置的值 int sorted maxn 排好序的陣列,方便尋找中值 int l...

主席樹求區間第k大

主席樹是可持久化線段樹,維護 權值個數 線段樹的字首和。相當於對每個區間 1,i 建立n顆線段樹。我們用乙個區間內的數的出現個數建線段樹,所以資料大小較大時一般進行離散化。建的是權值線段樹,即用數值作為區間,每個節點存該數出現的次數,所以query返回的其實是離散後的陣列b的下標idx,最終結果為b...

快樂找到區間第K大 劃分樹

劃分樹,顧名思義就是將乙個序列,劃分成很多小部分。其作用就是可以快樂地找到給定區間第k大的數。其實使用歸併樹和快排也都可以找到區間內第k大的數,但其效率都不如劃分樹要好,快排過的時間複雜度o n x m 而劃分樹是o n x logn 劃分樹原理 1.建樹 根結點就是原序列,左孩子儲存父結點所有元素...