二叉排序樹(shù)的建立辩块、查找、刪除

二叉排序樹(shù)又稱(chēng)為二叉查找樹(shù)荆永,具備以下性質(zhì):
①若它的左子樹(shù)不空废亭,則左子樹(shù)上所有結(jié)點(diǎn)的值均小于它的根結(jié)點(diǎn)的值;
②若它的右子樹(shù)不空具钥,則右子樹(shù)上所有結(jié)點(diǎn)的值均大于它的根結(jié)點(diǎn)的值豆村;
③它的左、右子樹(shù)也分別為二叉排序樹(shù)骂删;

二叉排序樹(shù)使用鏈?zhǔn)酱鎯?chǔ)掌动,插入和刪除的效率較好,又可以較高效的實(shí)現(xiàn)查找某個(gè)元素的功能桃漾。

中序遍歷二叉排序樹(shù)時(shí)能夠得到一個(gè)有序序列坏匪。

測(cè)試二叉排序樹(shù).png

二叉排序樹(shù)結(jié)點(diǎn)類(lèi)

/**
* @author Shaw
* @version 創(chuàng)建時(shí)間:2018年12月9日 下午3:33:43 
* 類(lèi)說(shuō)明:二叉排序樹(shù)結(jié)點(diǎn)類(lèi)
 */
class BiTNode {
    //數(shù)據(jù)域
    private int data;
    //左右結(jié)點(diǎn)域
    private BiTNode lchild, rchild;
    
    public BiTNode(int data) {
        this.data = data;
    }
    public int getData() {
        return data;
    }
    public void setData(int data) {
        this.data = data;
    }
    public BiTNode getLchild() {
        return lchild;
    }
    public void setLchild(BiTNode lchild) {
        this.lchild = lchild;
    }
    public BiTNode getRchild() {
        return rchild;
    }
    public void setRchild(BiTNode rchild) {
        this.rchild = rchild;
    }
    
    @Override
    public String toString() {
        return "BiTNode [data=" + data + ", lchild=" + lchild + ", rchild=" + rchild + "]";
    }   
}

二叉排序樹(shù)核心類(lèi)

二叉排序樹(shù)的建立就是不斷執(zhí)行插入操作的過(guò)程。

二叉排序樹(shù)的刪除分為三種情況討論:
①當(dāng)刪除結(jié)點(diǎn)僅有左子樹(shù)時(shí)撬统,只需將此結(jié)點(diǎn)的左孩子替換它自己适滓,就相當(dāng)于刪除了該結(jié)點(diǎn)。
②當(dāng)刪除結(jié)點(diǎn)僅有右子樹(shù)時(shí)恋追,只需將此結(jié)點(diǎn)的右孩子替換它自己即可凭迹。
③當(dāng)刪除結(jié)點(diǎn)左右子樹(shù)都不為空時(shí),可以在左子樹(shù)中找到小于但最接近該值的結(jié)點(diǎn)替換它苦囱,即找到中序遍歷中的前驅(qū)嗅绸;也可以在右子樹(shù)中找到大于但最接近該值的結(jié)點(diǎn)替換,即中序遍歷中的后驅(qū)撕彤。
本例中采用的是前驅(qū)替換鱼鸠。

/**
 * 
 * @author Shaw
 * @version 創(chuàng)建時(shí)間:2018年12月9日 下午3:34:11 
 * 類(lèi)說(shuō)明:二叉排序樹(shù)類(lèi)
 */
class BinarySortTree {
    public BiTNode root;

    public BinarySortTree() {
        root = null;
    }

    // 中序遍歷
    public void InOrderTraverse(BiTNode node) {
        if (node == null) {
            return;
        }
        InOrderTraverse(node.getLchild());
        System.out.print(node.getData() + " ");
        InOrderTraverse(node.getRchild());
    }

    // 二叉排序樹(shù)查找
    public BiTNode search_BST(int key) {
        BiTNode current = root;
        while (current != null) {
            // 查找成功返回對(duì)應(yīng)結(jié)點(diǎn)
            if (key == current.getData()) {
                return current;
            } else if (key < current.getData()) {
                current = current.getLchild();
            } else {
                current = current.getRchild();
            }
        }
        return null;
    }

    // 二叉排序樹(shù)插入
    public void insert_BST(int key) {
        // 空樹(shù)情況
        if (root == null) {
            root = new BiTNode(key);
            return;
        }
        // 結(jié)點(diǎn)已在樹(shù)中存在時(shí)
        if (search_BST(key) != null) {
            return;
        }
        BiTNode node = new BiTNode(key);
        BiTNode current = root;
        BiTNode parent = null;
        // 循環(huán)獲取待插入結(jié)點(diǎn)的父結(jié)點(diǎn)位置
        while (current != null) {
            parent = current;
            if (key < current.getData()) {
                current = current.getLchild();
            } else if (key > current.getData()) {
                current = current.getRchild();
            } else {
                break;
            }
        }
        // 判斷插入的是左結(jié)點(diǎn)還是右結(jié)點(diǎn)
        if (key < parent.getData()) {
            parent.setLchild(node);
        } else {
            parent.setRchild(node);
        }
    }

    // 二叉排序樹(shù)刪除
    public boolean delete_BST(int key) {
        // current結(jié)點(diǎn)指向待刪除的結(jié)點(diǎn)
        BiTNode current = root;
        // parent結(jié)點(diǎn)指向待刪除結(jié)點(diǎn)的父節(jié)點(diǎn)
        BiTNode parent = null;
        // 通過(guò)循環(huán)查找確定current和parent結(jié)點(diǎn)
        while (current != null) {
            // 待刪除的結(jié)點(diǎn)的值小于根結(jié)點(diǎn)的值猛拴,查找左子樹(shù)
            if (key < current.getData()) {
                parent = current;
                current = current.getLchild();
            }
            // 待刪除的結(jié)點(diǎn)的值小于根結(jié)點(diǎn)的值,查找右子樹(shù)
            else if (key > current.getData()) {
                parent = current;
                current = current.getRchild();
            }
            // 查找到結(jié)點(diǎn)后退出
            else {
                break;
            }
        }
        // 空樹(shù)的情況
        if (current == null) {
            return false;
        }
        // 右子樹(shù)為空的情況
        // 待刪除結(jié)點(diǎn)的左結(jié)點(diǎn)"繼承"該結(jié)點(diǎn)的位置
        if (current.getRchild() == null) {
            // 不是空樹(shù)的情況
            if (parent != null) {
                // 待刪除的結(jié)點(diǎn)是父節(jié)點(diǎn)的左結(jié)點(diǎn)
                if (parent.getLchild() == current) {
                    // 將待刪除結(jié)點(diǎn)的左結(jié)點(diǎn)設(shè)置為父結(jié)點(diǎn)的左結(jié)點(diǎn)
                    parent.setLchild(current.getLchild());
                } else {
                    // 待刪除的結(jié)點(diǎn)是父結(jié)點(diǎn)的右結(jié)點(diǎn)時(shí)將該結(jié)點(diǎn)的左結(jié)點(diǎn)設(shè)置為父結(jié)點(diǎn)的右結(jié)點(diǎn)
                    parent.setRchild(current.getLchild());
                }
            } else {
                // 空樹(shù)時(shí)將左結(jié)點(diǎn)賦值給根結(jié)點(diǎn)
                root = current.getLchild();
            }
        }
        // 左子樹(shù)為空的情況
        // 待刪除結(jié)點(diǎn)的右結(jié)點(diǎn)"繼承"該結(jié)點(diǎn)的位置
        else if (current.getLchild() == null) {
            if (parent != null) {
                if (parent.getLchild() == current) {
                    parent.setLchild(current.getLchild());
                } else {
                    parent.setRchild(current.getRchild());
                }
            } else {
                root = current.getRchild();
            }
        }
        // 左右子樹(shù)均不為空的情況
        // 在二叉排序樹(shù)中序中選擇該結(jié)點(diǎn)的前驅(qū)或者后繼替換該結(jié)點(diǎn)蚀狰,
        // 也就是選擇比該結(jié)點(diǎn)小或者比它大的最接近的兩個(gè)數(shù)中的一個(gè)愉昆,本例選擇的是比該結(jié)點(diǎn)小的結(jié)點(diǎn)
        else {
            // 用于替換的結(jié)點(diǎn)
            BiTNode replaceNode = current.getLchild();
            // 替換結(jié)點(diǎn)的父節(jié)點(diǎn),初始化為待刪除的結(jié)點(diǎn)
            BiTNode replaceParentNode = current;
            // 用于替換的結(jié)點(diǎn)存在右子結(jié)點(diǎn)時(shí),因?yàn)橛医Y(jié)點(diǎn)大于根結(jié)點(diǎn)的值麻蹋,所以右子結(jié)點(diǎn)更接近被刪除的結(jié)點(diǎn)
            while (replaceNode.getRchild() != null) {
                replaceParentNode = replaceNode;
                replaceNode = replaceNode.getRchild();
            }
            // 替換結(jié)點(diǎn)不存在右子結(jié)點(diǎn)時(shí)
            // 相等時(shí)由于使用的是前驅(qū)值代替跛溉,所以需要補(bǔ)充的是左子結(jié)點(diǎn)的值
            if (replaceParentNode == current) {
                replaceParentNode.setLchild(replaceNode.getLchild());
            }
            // replaceParentNode與current不相等時(shí)
            // replaceNode肯定是大于replaceParentNode的值的,所以需要補(bǔ)充的是右子結(jié)點(diǎn)的值
            else {
                replaceParentNode.setRchild(replaceNode.getLchild());
            }
            // 替換對(duì)應(yīng)的值
            current.setData(replaceNode.getData());
        }
        return true;
    }
}

測(cè)試程序:

public class BinarySortTreeMain {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int[] a = { 62, 88, 58, 47, 35, 73, 51, 99, 37, 93, 36, 39, 42, 62 };
        BinarySortTree binarySortTree = new BinarySortTree();
        System.out.println("原始序列:");
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + " ");
            binarySortTree.insert_BST(a[i]);
        }
        System.out.println("\n二叉排序后的序列:");
        binarySortTree.InOrderTraverse(binarySortTree.root);
        System.out.print("\n查找37:");
        BiTNode node = binarySortTree.search_BST(37);
        if (node != null) {
            System.out.println(true);
        } else {
            System.out.println(false);
        }
        System.out.print("查找22:");
        node = binarySortTree.search_BST(22);
        if (node != null) {
            System.out.println(true);
        } else {
            System.out.println(false);
        }
        System.out.print("刪除47:");
        System.out.println(binarySortTree.delete_BST(47));
        System.out.println("刪除47后的序列:");
        binarySortTree.InOrderTraverse(binarySortTree.root);
        System.out.print("\n刪除22:");
        System.out.println(binarySortTree.delete_BST(22));
    }
}

測(cè)試結(jié)果:

二叉排序樹(shù)測(cè)試結(jié)果圖.png

總結(jié)

二叉排序樹(shù)以鏈接方式存儲(chǔ),保持了鏈接存儲(chǔ)結(jié)構(gòu)在執(zhí)行插入或刪除操作時(shí)不用移動(dòng)元素的優(yōu)點(diǎn)扮授,插入刪除的時(shí)間性能較好芳室。對(duì)于二叉排序樹(shù)的查找,比較次數(shù)等于給定值的結(jié)點(diǎn)在二叉排序樹(shù)的層數(shù)刹勃。最少為1次堪侯,最多也不會(huì)超過(guò)樹(shù)的深度。

缺點(diǎn):當(dāng)本例中的數(shù)組是{35,36,37,39,42,47,51,58,73,88,93,99}時(shí)深夯,即數(shù)組元素從小到大有序時(shí)抖格,此時(shí)的二叉排序樹(shù)如圖所示是一棵右斜樹(shù)。當(dāng)同樣查找99結(jié)點(diǎn)時(shí)咕晋,測(cè)試?yán)有枰?次比較雹拄,本例卻需要12次比較,二者差異很大掌呜。也就是說(shuō)滓玖,二叉排序樹(shù)的查找性能主要取決于二叉排序樹(shù)的形狀,但本例中的二叉排序樹(shù)的形狀是由給定數(shù)組來(lái)構(gòu)造的质蕉,即形狀是不確定的势篡。為此,我們需要構(gòu)造平衡二叉樹(shù)來(lái)解決這個(gè)問(wèn)題模暗。

右斜樹(shù).png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末禁悠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子兑宇,更是在濱河造成了極大的恐慌碍侦,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隶糕,死亡現(xiàn)場(chǎng)離奇詭異瓷产,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)枚驻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)濒旦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人再登,你說(shuō)我怎么就攤上這事尔邓×榔剩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵铃拇,是天一觀的道長(zhǎng)钞瀑。 經(jīng)常有香客問(wèn)我,道長(zhǎng)慷荔,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任缠俺,我火速辦了婚禮显晶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘壹士。我一直安慰自己磷雇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布躏救。 她就那樣靜靜地躺著唯笙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪盒使。 梳的紋絲不亂的頭發(fā)上崩掘,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音少办,去河邊找鬼苞慢。 笑死,一個(gè)胖子當(dāng)著我的面吹牛英妓,可吹牛的內(nèi)容都是我干的挽放。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蔓纠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼辑畦!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起腿倚,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤纯出,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后猴誊,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體潦刃,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年懈叹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乖杠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡澄成,死狀恐怖胧洒,靈堂內(nèi)的尸體忽然破棺而出畏吓,到底是詐尸還是另有隱情,我是刑警寧澤卫漫,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布瘩例,位于F島的核電站,受9級(jí)特大地震影響达布,放射性物質(zhì)發(fā)生泄漏骇窍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一包吝、第九天 我趴在偏房一處隱蔽的房頂上張望饼煞。 院中可真熱鬧,春花似錦诗越、人聲如沸砖瞧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)块促。三九已至,卻和暖如春床未,著一層夾襖步出監(jiān)牢的瞬間竭翠,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工即硼, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逃片,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓只酥,卻偏偏與公主長(zhǎng)得像褥实,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子裂允,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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