Java B+ 樹(shù) 簡(jiǎn)單實(shí)現(xiàn)

本文暫時(shí)實(shí)現(xiàn)了B+樹(shù)插入锦积,查找和范圍查找功能,由于需求原因歉嗓,暫未實(shí)現(xiàn)刪除功能丰介。
本文B+樹(shù)Key為int類型,Value支持泛型遥椿。
Github開(kāi)源地址:https://github.com/1161140118/HIT-DataBase-Lab4/tree/master/src
感激您的star


數(shù)據(jù)結(jié)構(gòu):

主類BPlusTree基矮,維護(hù)root結(jié)點(diǎn)。
非葉結(jié)點(diǎn)Node冠场,維護(hù)Key和子節(jié)點(diǎn)索引家浇。
葉結(jié)點(diǎn)Leaf,維護(hù)Key和Value索引碴裙。

主體思路:

主類BPlusTree維護(hù)root結(jié)點(diǎn)钢悲。
葉結(jié)點(diǎn)記錄父節(jié)點(diǎn)点额,非根中間結(jié)點(diǎn)記錄父節(jié)點(diǎn)。

初始狀態(tài)下莺琳,root結(jié)點(diǎn)為Node还棱,僅記錄1個(gè)索引唯一葉子。
插入開(kāi)始惭等,由root->leaf珍手,插入到leaf;
隨著插入進(jìn)行辞做,leaf發(fā)生分裂琳要,中間結(jié)點(diǎn)Key拷貝上溢到父節(jié)點(diǎn)Node類型結(jié)點(diǎn)。
倒數(shù)第二層的Node結(jié)點(diǎn)的分裂由Leaf分裂上溢引發(fā)秤茅,

Node結(jié)點(diǎn)的分裂分為兩種情況處理:

1. Node結(jié)點(diǎn)為根節(jié)點(diǎn)
下推:
當(dāng)前根節(jié)點(diǎn)分裂為兩個(gè)新子節(jié)點(diǎn)left和right稚补,分裂依據(jù)的Key稱為splitKey。
初始化當(dāng)前結(jié)點(diǎn)框喳,即重置當(dāng)前結(jié)點(diǎn)的Key List只包含splitKey课幕,而子節(jié)點(diǎn)引用只包含left和right。
即五垮,當(dāng)前節(jié)點(diǎn)仍未根節(jié)點(diǎn)乍惊,但Key和子節(jié)點(diǎn)引用都發(fā)生了改變。
2. Node結(jié)點(diǎn)為非根結(jié)點(diǎn)
上溢:
當(dāng)前節(jié)點(diǎn)分裂出一個(gè)新節(jié)點(diǎn)拼余,在父節(jié)點(diǎn)中插入splitKey和新節(jié)點(diǎn)的索引污桦。

主類BPlusTree數(shù)據(jù)結(jié)構(gòu):

public class BPlusTree<V> {
    public static int rank;
    private Node<V> root;
    
    public BPlusTree(int rank) {
        BPlusTree.rank = rank;
        // 根節(jié)點(diǎn)始終維護(hù)同一個(gè)Node
        this.root = new Node<V>(null, false);
    }
    
    public void insertData(int key,V ref) {
        root.insertData(key, ref);
    }
    
    
    /**
     * 遞歸調(diào)用Node.search方法,查詢索引
     * @param key 索引鍵
     * @return
     */
    public V search(int key) {
        return root.search(key);
    }
    
    /**
     * range search from start to end
     * @param start start key, include 
     * @param end   end key, include
     * @return  value list
     */
    public List<V> rangeSearch(int start,int end){
        return root.rangeSearch(start, end);
    }

插入部分算法
非葉結(jié)點(diǎn)Node:上溢匙监,或下推

    /**
     * 子節(jié)點(diǎn)分裂時(shí)凡橱,由子節(jié)點(diǎn)調(diào)用
     * @param key 待插入關(guān)鍵字
     */
    protected void insertNode(int key, Node<V> node) {
        System.out.println("非葉節(jié)點(diǎn)插入 " + key);
        int i = 0;
        for (i = 0; i < keyList.size(); i++) {
            if (key < keyList.get(i)) {
                break;
            }
        }
        keyList.add(i, key);
        childNodes.add(i + 1, node);
        /**
         * 判定分裂
         * 非根節(jié)點(diǎn):上溢分裂
         * 根節(jié)點(diǎn):下推分裂,當(dāng)前節(jié)點(diǎn)分裂為兩個(gè)新節(jié)點(diǎn)亭姥,當(dāng)前節(jié)點(diǎn)重置稼钩,新節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn)子節(jié)點(diǎn)
         */
        if (keyList.size() >= BPlusTree.rank) {
            System.out.println("非葉節(jié)點(diǎn)分裂." + key);
            int split = BPlusTree.rank / 2;
            Integer splitKey = keyList.get(split);
            // 新節(jié)點(diǎn):左右子樹(shù)
            List<Integer> leftKeyList = new LinkedList<>();
            List<Node<V>> leftChilds = new LinkedList<>();
            List<Integer> rightKeyList = new LinkedList<>();
            List<Node<V>> rightChilds = new LinkedList<>();
            // copy
            for (int j = 0; j < split; j++) {
                leftKeyList.add(keyList.get(j));
                leftChilds.add(childNodes.get(j));
            }
            leftChilds.add(childNodes.get(split));
            for (int j = split + 1; j < keyList.size(); j++) {
                rightKeyList.add(keyList.get(j));
                rightChilds.add(childNodes.get(j));
            }
            rightChilds.add(childNodes.get(keyList.size()));


            this.keyList.clear();
            this.childNodes.clear();
            Node<V> leftChild = new Node<V>(this, leftKeyList, leftChilds);
            Node<V> rightChild = new Node<V>(this, rightKeyList, rightChilds);

            if (parent != null) {
                // 上溢
                this.keyList = leftKeyList;
                this.childNodes = leftChilds;
                parent.insertNode(splitKey, rightChild);
                System.out.println("非葉節(jié)點(diǎn)上溢 "+splitKey);
            } else {
                // 根節(jié)點(diǎn)下推
                // 更新當(dāng)前結(jié)點(diǎn),分裂后下推
                this.keyList.add(splitKey);
                this.childNodes.add(leftChild);
                this.childNodes.add(rightChild);
                System.out.println("根節(jié)點(diǎn)下推"+splitKey);
            }
        }
    }

葉結(jié)點(diǎn)Leaf:上溢

    /**
     * 在葉結(jié)點(diǎn)插入索引达罗,
     * 可能觸發(fā)分裂
     * @return 
     */
    @Override
    protected void insertData(int key, V ref) {
        int i = 0;
        for (i = 0; i < refList.size(); i++) {
            if (key < keyList.get(i)) {
                break;
            }
        }
        keyList.add(i, key);
        refList.add(i, ref);
        System.out.println("葉結(jié)點(diǎn)插入"+key);
        
        /**
         * 判定分裂
         * 例:4階樹(shù)坝撑,達(dá)到4個(gè)數(shù)據(jù)結(jié)點(diǎn),分裂粮揉,上溢第三個(gè)key(index=2) 
         */
        if (refList.size() >= BPlusTree.rank) {
            int split = BPlusTree.rank / 2;
            List<Integer> newKeyList = new LinkedList<>();
            List<V> newRefList = new LinkedList<>();
            // copy
            for (int j = split; j < keyList.size(); j++) {
                newKeyList.add(keyList.get(j));
                newRefList.add(refList.get(j));
            }
            // remove
            int len  = keyList.size();
            for (int j = split; j < len; j++) {
                // 執(zhí)行 size-split 此
                keyList.remove(split);
                refList.remove(split);
            }
            Leaf<V> newLeaf = new Leaf<V>(parent, newKeyList, newRefList);
            this.next = newLeaf;
            System.out.println("葉結(jié)點(diǎn)分裂."+key);
            parent.insertNode(newKeyList.get(0), newLeaf);
        }
        System.out.println(key+" 插入最終結(jié)果 "+keyList);
        // 檢查不變量
        if (keyList.size() != refList.size()) {
            System.err.println("Error : The length of keylist not equals to reflist!");
        }
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末巡李,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子扶认,更是在濱河造成了極大的恐慌侨拦,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辐宾,死亡現(xiàn)場(chǎng)離奇詭異狱从,居然都是意外死亡膨蛮,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門季研,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)敞葛,“玉大人,你說(shuō)我怎么就攤上這事与涡∪切常” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵递沪,是天一觀的道長(zhǎng)豺鼻。 經(jīng)常有香客問(wèn)我,道長(zhǎng)款慨,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任谬莹,我火速辦了婚禮檩奠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘附帽。我一直安慰自己埠戳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布蕉扮。 她就那樣靜靜地躺著整胃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天疏日,我揣著相機(jī)與錄音独令,去河邊找鬼。 笑死轧苫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酬蹋,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼抽莱!你這毒婦竟也來(lái)了范抓?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤食铐,失蹤者是張志新(化名)和其女友劉穎匕垫,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體璃岳,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡年缎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年悔捶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片单芜。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蜕该,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出洲鸠,到底是詐尸還是另有隱情堂淡,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布扒腕,位于F島的核電站绢淀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏瘾腰。R本人自食惡果不足惜皆的,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蹋盆。 院中可真熱鬧费薄,春花似錦、人聲如沸栖雾。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)析藕。三九已至召廷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間账胧,已是汗流浹背竞慢。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留找爱,地道東北人梗顺。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像车摄,于是被迫代替她去往敵國(guó)和親寺谤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

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