來自crush的中序遍歷完全二叉樹

2021-09-16 12:40:25 字數 2747 閱讀 7402

最近研究ceph中的crush演算法,發現其tree型bucket所使用的一種以前沒見過的結構,畫了畫發現是中序遍歷的樣子,**原文是這樣介紹的:

大概作用就是保證已有節點的位置不變,樹擴大時可以直接成倍擴容等。

我看了**發現具體實現上還是不盡完美,於是在這裡稍微整理下這種用法。

可以發現很多性質:

葉子都是奇數,奇數都是葉子

這點非常好用,假設我們用它來實現線段樹,那麼我們關注的就是葉子,而這條性質可以通過2*i+1快速定位到葉子節點,zkw線段樹是先找好起點再定位,但是zkw線段樹不能隨意的擴容。

每層的節點lowbit值相同

lowbit是樹狀陣列裡提到的概念,就是取數字的最後一位1,例如6=110,lowbit(6)=10=2。

很容易發現這種樹形結構的底層lowbit為1,上一層為2,然後是4......

//也就是第一層是1的倍數,第二層是2的倍數.....

標準的平衡樹

這種樹結構看起來就是非常標準的平衡二叉樹,左小右大,高度平衡。

//中序遍歷的結果

我們假定樹的大小是size,則size一定是2^n-1,我們規定乙個點的高度h(x)=lowbit(x)

樹根中序遍歷的完全二叉樹,樹根一定是(size+1)/2,即root=2^(n-1)

反之,若樹根是root,則size=root*2-1

樹的大小

其實很容易發現樹根的值就是葉子的個數,而樹根又是2的次冪,所以只要找到第乙個大於等於需要葉子樹的2的次冪作根即可

子節點觀察總結後發現:

左兒子=x-h(x)/2

右兒子=x+h(x)/2

那麼定位到子節點就很容易了:

lson(x)=x-(h(x)>>1)

rson(x)=x+(h(x)>>1)

左?右?

父節點由上兩條已經很清楚了:

lson_father=x+h(x)

rson_father=x-h(x)

兄弟節點

順便可以得到兄弟節點:

l_sibling=x+(h(x)<<1)

r_sibling=x-(h(x)<<1)

也就是 x^(h(x)<<1)

而且father其實就是左右兒子之和的一半,即 father(x)=(x+sibling(x))>>1

樹的增長

當節點數增加,葉子數量不夠了,則原地翻倍增長,即root<<=1,輕鬆翻倍

於是訪問這棵樹的方法就基本全了。

下面是普通的線段樹解決區間最值的**:

#include#define low(x)  (x&-x)

#define hi(x) (low(x)<<1)

#define is_r(x) (x&hi(x))

#define sib(x) (x^hi(x))

#define fa(x) ((x|hi(x))^low(x))

#define pos(x) ((x<<1)|1)

using namespace std;

using ll=long long;

const int maxn=500500;

int n,m,a[maxn],root,x,y;

void up(int x, int y)

int ask(int x, int y)

void init()

}void work()

}int main()

乙個節點的管轄範圍

只要不斷的迭代自己的左右兒子就能得到自己最左的葉子和最右的葉子,即:

l_leaf=x-h(x)+1

r_leaf=x+h(x)-1

應用根據上條性質,容易聯想到一般的線段樹中,遞迴時需要傳遞目前樹的管轄範圍,然而本樹不需要。

這種樹的好性質在於知道乙個節點的編號,它的具體位置、父節點、兄弟節點、左右子節點、管轄範圍等都可以直接算出來,而且很快,幾乎是位運算和1次加法。

下面放上帶lazy的線段樹寫法:

#include#define print(x...) printf(x) 

using namespace std;

const int maxn=4000005;

int a[maxn],root,n,m,x,y,z,b[maxn];

int height(int x)

bool is_left(int x)

int father(int x)

int lson(int x)

int rson(int x)

int sibling(int x)

int ll(int x)

int rr(int x)

int pos(int x)

void init(int x)

if(ll(s)>y || rr(s)=x && rr(s)<=y)return a[s];

if(ll(s)>y || rr(s)=x)else}}

這種基於中序遍歷的完全二叉樹天然支援葉子定位和空間動態伸縮,寫起來也很方便,初始化極為便利,基本不需要任何操作,計算樹根的值即可。實際上這種樹時無限大的,我們每次都是取它「左下角」的一塊來使用。相比於層次遍歷結構的二叉樹,該種類對處理葉子縱向增長更輕鬆方便,比如說用這種結構實現個堆就會比較麻煩,至少不如層次型的方便。不過仍有很多適用的地方。

二叉查詢樹的先序遍歷,中序遍歷,後序遍歷

1 有乙個二叉查詢樹,儲存者字元 a b c d e f g h 下面哪個結果是後序樹遍歷結果 a.adbcegfh b.bcagehfd c.bcaefdhg d.bdacefhg 我的結題思路是將每個答案按照後序的遍歷方法把二叉樹儲存資料的結構還原,看是否滿足二叉樹的性質。二叉樹的性質 1 左子...

樹的遍歷 先序遍歷 中序遍歷 後序遍歷

名詞解釋 1 每個元素稱為節點 2 有乙個特定的節點被稱為根節點或樹根 3 除根節點外的其餘資料元素被分為m個互不相交的集合t1,t2,t3.tm 1,其中每乙個集合ti本身也是乙個樹,被稱作原樹的子樹 節點的度 乙個節點含有子樹的個數稱為該節點的度 葉節點或終端節點 度為0的節點稱為葉節點 非終端...

二叉樹遍歷(先序遍歷 中序遍歷 後續遍歷)

1 先序遍歷也叫做先根遍歷 前序遍歷,可記做根左右 二叉樹父結點向下先左後右 2 首先訪問根結點然後遍歷左子樹,最後遍歷右子樹。在遍歷左 右子樹時,仍然先訪問根結點,然後遍歷左子樹,最後遍歷右子樹,如果二叉樹為空則返回。非遞迴遍歷 param b public static void prescan...