區(qū)塊鏈智能合約Append-only B-tree

本文考慮使用區(qū)塊鏈智能合約solidity語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單B樹(shù)的構(gòu)建腺怯、插入元素方法和查詢方法轨奄。B樹(shù)的實(shí)現(xiàn)難點(diǎn)在于結(jié)點(diǎn)的分裂的操作裳仆、分裂的判斷、元素的移動(dòng)等脊另。智能合約實(shí)現(xiàn)的難點(diǎn)在于solidity語(yǔ)言中不存在‘指針’這一數(shù)據(jù)結(jié)構(gòu)导狡,增加了對(duì)于依賴指針的樹(shù)狀數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)難度≠送矗考慮可以使用mapping來(lái)存儲(chǔ)結(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)旱捧,使用數(shù)組來(lái)存放child結(jié)點(diǎn)的鍵。進(jìn)而達(dá)到使用mapping+數(shù)組下標(biāo)來(lái)替代指針的目的踩麦。

struct node{
        uint[] keys;
        uint[] child;
        bool leaf;
        uint8 n;
    }

在結(jié)構(gòu)體node當(dāng)中枚赡,keys存放數(shù)據(jù)的鍵,child存放此結(jié)點(diǎn)的孩子結(jié)點(diǎn)的下標(biāo)谓谦,leaf代表此節(jié)點(diǎn)是否為葉子節(jié)點(diǎn)贫橙,n為此結(jié)點(diǎn)中已經(jīng)存放的key的數(shù)目。

uint root = 0;
uint8 t=2;
mapping(uint=>node) tree;

同時(shí)反粥,為了實(shí)現(xiàn)的方便卢肃,還需要存儲(chǔ)B樹(shù)的根節(jié)點(diǎn)的下標(biāo)root疲迂。B樹(shù)的度t也需要單獨(dú)列出。mapping為存放樹(shù)節(jié)點(diǎn)的映射莫湘。
Properties of B-Tree

  1. All leaves are at same level.
  2. A B-Tree is defined by the term minimum degree ‘t’. The value of t depends upon disk block size.
  3. Every node except root must contain at least t-1 keys. Root may contain minimum 1 key.
  4. All nodes (including root) may contain at most 2t – 1 keys.
  5. Number of children of a node is equal to the number of keys in it plus 1.
  6. All keys of a node are sorted in increasing order. The child between two keys k1 and k2 contains all keys in range from k1 and k2.
  7. B-Tree grows and shrinks from root which is unlike Binary Search Tree. Binary Search Trees grow downward and also shrink from downward.
  8. Like other balanced Binary Search Trees, time complexity to search, insert and delete is O(Logn).

Insertion
1) Initialize x as root.
2) While x is not leaf, do following
..a) Find the child of x that is going to to be traversed next. Let the child be y.
..b) If y is not full, change x to point to y.
..**c) **If y is full, split it and change x to point to one of the two parts of y. If k is smaller than mid key in y, then set x as first part of y. Else second part of y. When we split y, we move a key from y to its parent x.
3) The loop in step 2 stops when x is leaf. x must have space for 1 extra key as we have been splitting all nodes in advance. So simply insert k to x.

Note that the algorithm follows the Cormen book. It is actually a proactive insertion algorithm where before going down to a node, we split it if it is full. The advantage of splitting before is, we never traverse a node twice. If we don’t split a node before going down to it and split it only if new key is inserted (reactive), we may end up traversing all nodes again from leaf to root. This happens in cases when all nodes on the path from root to leaf are full. So when we come to the leaf node, we split it and move a key up. Moving a key up will cause a split in parent node (because parent was already full). This cascading effect never happens in this proactive insertion algorithm. There is a disadvantage of this proactive insertion though, we may do unnecessary splits.

Let us understand the algorithm with an example tree of minimum degree ‘t’ as 3 and a sequence of integers 10, 20, 30, 40, 50, 60, 70, 80 and 90 in an initially empty B-Tree.

Initially root is NULL. Let us first insert 10.

Let us now insert 20, 30, 40 and 50. They all will be inserted in root because maximum number of keys a node can accommodate is 2*t – 1 which is 5.

Let us now insert 60. Since root node is full, it will first split into two, then 60 will be inserted into the appropriate child.

Let us now insert 70 and 80. These new keys will be inserted into the appropriate leaf without any split.

Let us now insert 90. This insertion will cause a split. The middle key will go up to the parent.

pragma solidity ^0.4.11;

contract btree{
    uint root = 0; //root pointer index
    uint8 constant t=3; //minimum degree
    struct node{
        uint[2*t-1] keys; // An array of keys
        uint[2*t] child; //child pointers
        bool leaf; //boolean whether it is a leaf node
        uint8 n; // current key number
    }
    
    
    mapping(uint=>node) tree; //storage of tree nodes
    
    function ssindex(uint id,uint lb,uint ub)internal constant returns(uint i,uint j){
        node tt = tree[id];
        i=0;
        
        while(i<tt.n && lb>tt.keys[i]){
            i++;
        }
        j=i;
        while(j<tt.n && ub>tt.keys[j]){
            j++;
        }
    }
    
    function range(uint lb, uint ub)constant public returns(uint){
        return _range(root,lb,ub);
    }
    function _range(uint id,uint lb, uint ub) internal constant returns(uint){
        node storage tt = tree[id];
        uint r = 0;
        if(ub<tt.keys[0] || lb>tt.keys[tt.n-1]){
            
        }
        else{
            for(uint i=0;i<tt.n;i++){
                if(tt.keys[i]>=lb && tt.keys[i] <=ub){
                    r += tt.keys[i];
                }
            }
        }
        
        if(tt.leaf==false){
            uint lbi;
            uint ubi;
            (lbi,ubi) = ssindex(id,lb,ub);
            for(i=lbi;i<=ubi;i++){
                r += _range(tt.child[i],lb,ub);
            }
        }
        
        return r;
    }
    
    // Function to search key k in subtree rooted with this node
    function _search(uint id,uint k)internal constant returns(uint ){
        uint i=0;
        while(i<tree[id].n && k > tree[id].keys[i]){
            i++;
        }
        if(tree[id].keys[i]==k){
            return k;
        }
        if(tree[id].leaf==true){
            return 0;
        }
        return _search(tree[id].child[i],k);
    }
    
    function search(uint k)public constant returns(uint){
        return _search(root,k);
    }
    
    function insert_list(uint[] l)public{
        for(uint i=0;i<l.length;i++){
            insert(l[i]);
        }
    }
    // The main function that inserts a new key in this B-Tree
    function insert(uint k) public{
        // If tree is empty
        if(root == 0){
            root = 2*k; //set an identifier of root
            uint[2*t-1] tk;
            uint[2*t] tc;
            tree[root] = node({
                keys: tk,
                child:tc,
                leaf:true,
                n:1
            }); //initilize a new node with specific parameters
            tree[root].keys[0]=k; //insert key k into root
        }
        else{
            //if root is full
            if(tree[root].n==(2*t-1)){
                //create a node node as the parent of the root
                uint s = 2*k;
                uint[2*t-1] tk2;
                uint[2*t] tc2;
                tree[s] = node({
                keys:tk2,
                child:tc2,
                leaf:false,
                n:0
            });
                //set root as the first child of node s
                tree[s].child[0] = root;
                //split the first child of s:root node
                splitchild(k,s,0);
                
                // New root has two children now.  Decide which of the
                // two children is going to have new key
                uint i=0;
                if(tree[s].keys[0]<k){
                    i++;
                }
                insertnonfull(tree[s].child[i],k);
                
                //change root
                root = s;
            }
            else{
                // If root is not full, call insertNonFull for root
                insertnonfull(root,k);
            }
        }
    }
    
    function splitchild(uint k,uint id,uint i)internal{
        uint z = 3*(id+i)*(k+1);//set a new identifier of the node z
        uint y = tree[id].child[i];
        // Create a new node which is going to store (t-1) keys
        // of y
        uint[2*t-1] tk;
        uint[2*t] tc;
        tree[z] = node({
                keys:tk,
                child:tc,
                leaf:tree[y].leaf,
                n:t-1
            });
        // Copy the last (t-1) keys of y to z
        for(uint j=0;j<t-1;j++){
            tree[z].keys[j] = tree[y].keys[j+t];
        }
        // Copy the last t children of y to z
        if(tree[y].leaf == false){
            for(j=0;j<t;j++){
                tree[z].child[j] = tree[y].child[j+t];
            }
        }
        // Reduce the number of keys in y
        tree[y].n = t-1;
        
        // Since this node is going to have a new child,
        // create space of new child
        for(j=tree[id].n;j>=i+1;j--){
            tree[id].child[j+1] = tree[id].child[j];
        }
        // Link the new child to this node
        tree[id].child[i+1]=z;
        
        // A key of y will move to this node. Find location of
        // new key and move all greater keys one space ahead
        if(tree[id].n>0){
            for(j=tree[id].n-1;j>=i&&j>=1;j--){
            tree[id].keys[j+1] = tree[id].keys[j];
            }
            if(j==0){
                tree[id].keys[j+1] = tree[id].keys[j];
            }
        }
        
        // Copy the middle key of y to this node
        tree[id].keys[i] = tree[y].keys[t-1];
        // Increment count of keys in this node
        tree[id].n = tree[id].n + 1;
    }
    
    // A utility function to insert a new key in this node
    // The assumption is, the node must be non-full when this
    // function is called
    function insertnonfull(uint id,uint k)internal{
        node storage tt = tree[id];
        // Initialize index as index of rightmost element
        uint i = tt.n-1;
        // If this is a leaf node
        if(tt.leaf == true){
            // The following loop does two things
            // a) Finds the location of new key to be inserted
            // b) Moves all greater keys to one place ahead
            while(i>=1 && tt.keys[i]>k){
                tt.keys[i+1] = tt.keys[i];
                i--;
            }
            if(i==0&&tt.keys[i]>k){
                // Insert the new key at found location
                tt.keys[i+1] = tt.keys[i];
                tt.keys[i] = k;
            }else{
                // Insert the new key at found location
                tt.keys[i+1] = k;
            }
            
            tt.n = tt.n+1;
        }
        else{// If this node is not leaf
            // Find the child which is going to have the new key
            while(i>=1 && tt.keys[i] > k){
                i--;
            }
            if(i==0 && tt.keys[i]>k){
                // See if the found child is full
                if(tree[tt.child[i]].n==(2*t-1)){
                    // If the child is full, then split it
                    splitchild(k,id,i);
                    // After split, the middle key of C[i] goes up and
                    // C[i] is splitted into two.  See which of the two
                    // is going to have the new key
                    if(tt.keys[i]<k)
                        i++;
                }
                insertnonfull(tt.child[i],k);
            }
            else{
                // See if the found child is full
                if(tree[tt.child[i+1]].n==(2*t-1)){
                    // If the child is full, then split it
                    splitchild(k,id,i+1);
                    // After split, the middle key of C[i] goes up and
                    // C[i] is splitted into two.  See which of the two
                    // is going to have the new key
                    if(tt.keys[i+1]<k)
                        i++;
                }
                insertnonfull(tt.child[i+1],k);
            }
        }
    }
    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尤蒿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子幅垮,更是在濱河造成了極大的恐慌腰池,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忙芒,死亡現(xiàn)場(chǎng)離奇詭異示弓,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)呵萨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)奏属,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人甘桑,你說(shuō)我怎么就攤上這事拍皮。” “怎么了跑杭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)咆耿。 經(jīng)常有香客問(wèn)我德谅,道長(zhǎng),這世上最難降的妖魔是什么萨螺? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任窄做,我火速辦了婚禮,結(jié)果婚禮上慰技,老公的妹妹穿的比我還像新娘椭盏。我一直安慰自己,他們只是感情好吻商,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布掏颊。 她就那樣靜靜地躺著,像睡著了一般艾帐。 火紅的嫁衣襯著肌膚如雪乌叶。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,158評(píng)論 1 308
  • 那天柒爸,我揣著相機(jī)與錄音准浴,去河邊找鬼。 笑死捎稚,一個(gè)胖子當(dāng)著我的面吹牛乐横,可吹牛的內(nèi)容都是我干的求橄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼葡公,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谈撒!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起匾南,我...
    開(kāi)封第一講書(shū)人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤啃匿,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蛆楞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體溯乒,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年豹爹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了裆悄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡臂聋,死狀恐怖光稼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情孩等,我是刑警寧澤艾君,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站肄方,受9級(jí)特大地震影響冰垄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜权她,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一虹茶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧隅要,春花似錦蝴罪、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至尼啡,卻和暖如春暂衡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背崖瞭。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工狂巢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人书聚。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓唧领,卻偏偏與公主長(zhǎng)得像藻雌,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子斩个,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,345評(píng)論 0 10
  • 中國(guó)銀行天津分行招聘實(shí)習(xí)生、管理培訓(xùn)生招聘人數(shù):10人要求:全日制大學(xué)本科及以上學(xué)歷滚局、大三以上年級(jí)每周確保3天以上...
    職道達(dá)人閱讀 136評(píng)論 0 0
  • 2017年10月13日 第一周的周末到來(lái)居暖,那種想馬上放松的心情立馬就蹦出來(lái),就像是囚犯突然獲得自由想馬上享受一般藤肢。...
    Me0327閱讀 161評(píng)論 0 0
  • 這幾天在路上移動(dòng)辦公太闺,非常懷念當(dāng)年寫(xiě)的一款工具JsonFormatter,不過(guò)當(dāng)時(shí)是在win下做android和x...
    a_mean閱讀 1,209評(píng)論 4 12