二叉查找樹 BinarySearchTree

二叉查找樹的定義:
二叉查找樹(Binary Search Tree)浩峡,是指一棵空樹或者具有下列性質(zhì)的二叉樹

  1. 若任意節(jié)點(diǎn)的左子樹不空,則左子樹上所有節(jié)點(diǎn)的值均小于它的根節(jié)點(diǎn)的值陡蝇;
  2. 若任意節(jié)點(diǎn)的右子樹不空,則右子樹上所有節(jié)點(diǎn)的值均大于它的根節(jié)點(diǎn)的值哮肚;
  3. 任意節(jié)點(diǎn)的左登夫、右子樹也分別為二叉查找樹;
  4. 沒有鍵值相等的節(jié)點(diǎn)允趟;

定義二叉查找樹的節(jié)點(diǎn)

/**
 * 二叉查找樹節(jié)點(diǎn)
 */
class BinaryTreeNode {

    public int value;
    public BinaryTreeNode left;
    public BinaryTreeNode right;

    public BinaryTreeNode(int value) {
        this.value = value;
    }

    public BinaryTreeNode(int value, BinaryTreeNode left, BinaryTreeNode right) {
        this.value = value;
        this.left = left;
        this.right = right;
    }
   //...
}

package com.hm.structure;

/**
 * Created by dmw on 2018/12/27.
 * Desc: 二叉查找樹
 * 二叉查找樹的定義:
 * 二叉查找樹(英語:Binary Search Tree)恼策,是指一棵空樹或者具有下列性質(zhì)的二叉樹
 * 1. 若任意節(jié)點(diǎn)的左子樹不空,則左子樹上所有節(jié)點(diǎn)的值均小于它的根節(jié)點(diǎn)的值拼窥;
 * 2. 若任意節(jié)點(diǎn)的右子樹不空戏蔑,則右子樹上所有節(jié)點(diǎn)的值均大于它的根節(jié)點(diǎn)的值;
 * 3. 任意節(jié)點(diǎn)的左鲁纠、右子樹也分別為二叉查找樹;
 * 4. 沒有鍵值相等的節(jié)點(diǎn)鳍寂。
 * <p>
 */
public class BinarySearchTree {

    public BinaryTreeNode root;

    public BinarySearchTree() {
    }

    public BinarySearchTree(BinaryTreeNode root) {
        this.root = root;
    }

    public BinaryTreeNode insert(int key) {
        BinaryTreeNode newNode = new BinaryTreeNode(key);
        BinaryTreeNode current = root;
        BinaryTreeNode parent;
        //如果根節(jié)點(diǎn)為空
        if (current == null) {
            root = newNode;
            return newNode;
        }
        while (true) {
            parent = current;
            if (key < current.value) {
                current = current.left;
                if (current == null) {
                    parent.left = newNode;
                    return newNode;
                }
            } else {
                current = current.right;
                if (current == null) {
                    parent.right = newNode;
                    return newNode;
                }
            }
        }
    }

    public BinaryTreeNode search(int key) {
        BinaryTreeNode current = root;
        while (current != null && key != current.value) {
            if (key < current.value) {
                current = current.left;
            } else {
                current = current.right;
            }
        }
        return current;
    }

    public BinaryTreeNode delete(int key) {
        BinaryTreeNode parent = root;
        BinaryTreeNode current = root;
        //標(biāo)記當(dāng)前節(jié)點(diǎn)是父節(jié)點(diǎn)的左節(jié)點(diǎn)還是右節(jié)點(diǎn)
        boolean isLeftChild = false;
        // 找到刪除節(jié)點(diǎn)及是否在左子樹
        while (current.value != key) {
            parent = current;
            if (current.value > key) {
                isLeftChild = true;
                current = current.left;
            } else {
                isLeftChild = false;
                current = current.right;
            }
            //如果要刪除的節(jié)點(diǎn)為null改含,直接返回
            if (current == null) {
                return current;
            }
        }

        // 如果刪除節(jié)點(diǎn)左節(jié)點(diǎn)為空 , 右節(jié)點(diǎn)也為空
        if (current.left == null && current.right == null) {
            if (current == root) {
                root = null;
            }
            if (isLeftChild) {
                parent.left = null;
            } else {
                parent.right = null;
            }
        } else if (current.right == null) {// 如果要刪除的節(jié)點(diǎn)只有左節(jié)點(diǎn)
            if (current == root) {
                root = root.left;
            } else if (isLeftChild) {
                parent.left = current.left;
            } else {
                parent.right = current.left;
            }
        } else if (current.left == null) {// 如果要刪除的節(jié)點(diǎn)只有右節(jié)點(diǎn)
            if (current == root) {
                root = current.right;
            } else if (isLeftChild) {
                parent.left = current.right;
            } else {
                parent.right = current.right;
            }
        } else {
            // 如果刪除節(jié)點(diǎn)左右子節(jié)點(diǎn)都不為空,則先找到待刪除節(jié)點(diǎn)的中序遍歷的后繼節(jié)點(diǎn),
            //用該后繼節(jié)點(diǎn)的值替換待刪除的節(jié)點(diǎn)的值迄汛,然后刪除后繼節(jié)點(diǎn)捍壤。
            BinaryTreeNode successor = getDeleteSuccessor(current);
            if (current == root) {
                root = successor;
            } else if (isLeftChild) {
                parent.left = successor;
            } else {
                parent.right = successor;
            }
            //將要刪除節(jié)點(diǎn)的左子節(jié)點(diǎn)賦值給后繼節(jié)點(diǎn)的左子節(jié)點(diǎn)
            successor.left = current.left;
        }
        //將刪除節(jié)點(diǎn)的子節(jié)點(diǎn)都置為null
        current.left = null;
        current.right = null;
        return current;
    }

    public void printTree(BinaryTreeNode root) {
        if (root != null) {
            printTree(root.left);
            System.out.print("" + root.value + " ");
            printTree(root.right);
        }
    }

    /**
     * 獲取刪除節(jié)點(diǎn)的后繼者,刪除節(jié)點(diǎn)的后繼者是在其右子樹中最小的節(jié)點(diǎn)
     *
     * @param deleteNode
     * @return
     */
    private BinaryTreeNode getDeleteSuccessor(BinaryTreeNode deleteNode) {
        //要刪除節(jié)點(diǎn)的后繼節(jié)點(diǎn)
        BinaryTreeNode successor = null;
        BinaryTreeNode successorParent = null;
        BinaryTreeNode current = deleteNode.right;
        while (current != null) {
            successorParent = successor;
            successor = current;
            current = current.left;
        }
        if (successor != deleteNode.right) {
            successorParent.left = successor.right;
            successor.right = deleteNode.right;
        }
        return successor;
    }


    public static void main(String[] args) {
        //BinaryTreeNode root = new BinaryTreeNode(8);
        BinarySearchTree treeTest = new BinarySearchTree();
        treeTest.insert(8);
        treeTest.insert(6);
        treeTest.insert(10);
        treeTest.insert(5);
        treeTest.insert(7);
        treeTest.insert(9);
        treeTest.insert(20);
        treeTest.insert(12);
        treeTest.insert(14);
        treeTest.insert(13);
        treeTest.printTree(treeTest.root);
        /*BinaryTreeNode deletedNode = treeTest.delete(10);
        System.out.println(deletedNode.value);*/
    }

}

插入和刪除都比較簡單鞍爱,下面我們演示一個刪除節(jié)點(diǎn)的操作

如下所示的一個二叉樹


BinarySearchTree0

我們想要刪除value為10的節(jié)點(diǎn)


BinarySearchTree1

因?yàn)橐獎h除節(jié)點(diǎn)左右子節(jié)點(diǎn)都不為空,則先找到待刪除節(jié)點(diǎn)的中序遍歷的后繼節(jié)點(diǎn)鹃觉,要刪除節(jié)點(diǎn)的后繼節(jié)點(diǎn)是在其右子樹中最小的節(jié)點(diǎn)。就是圖中value為12的節(jié)點(diǎn)睹逃。

/**
 * 獲取刪除節(jié)點(diǎn)的后繼者盗扇,刪除節(jié)點(diǎn)的后繼者是在其右子樹中最小的節(jié)點(diǎn)
 *
 * @param deleteNode
 * @return
 */
private BinaryTreeNode getDeleteSuccessor(BinaryTreeNode deleteNode) {
    //要刪除節(jié)點(diǎn)的后繼節(jié)點(diǎn)
    BinaryTreeNode successor = null;
    BinaryTreeNode successorParent = null;
    BinaryTreeNode current = deleteNode.right;
    while (current != null) {
        successorParent = successor;
        successor = current;
        current = current.left;
    }
    //找到后繼節(jié)點(diǎn)是12,不是要刪除節(jié)點(diǎn)的右子節(jié)點(diǎn)
    if (successor != deleteNode.right) {
        //步驟1
        successorParent.left = successor.right;
        //步驟2 
        successor.right = deleteNode.right;
    }
    return successor;
}

BinarySearchTree2
  1. 步驟1后圖譜如下


    BinarySearchTree3
  2. 步驟2后圖譜如下


    BinarySearchTree4

最后的調(diào)整階段

 if (current == root) {
      root = successor;
  } else if (isLeftChild) {
      parent.left = successor;
  } else {
      //進(jìn)入此分支
      parent.right = successor;
  }
  //將要刪除節(jié)點(diǎn)的左子節(jié)點(diǎn)賦值給后繼節(jié)點(diǎn)的左子節(jié)點(diǎn)
  successor.left = current.left;

調(diào)整后就刪除成功了沉填,結(jié)果如下圖所示


BinarySearchTree5

參考鏈接:
圖解:二叉搜索樹算法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疗隶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子翼闹,更是在濱河造成了極大的恐慌斑鼻,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猎荠,死亡現(xiàn)場離奇詭異坚弱,居然都是意外死亡蜀备,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門荒叶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琼掠,“玉大人,你說我怎么就攤上這事停撞〈赏埽” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵戈毒,是天一觀的道長艰猬。 經(jīng)常有香客問我,道長埋市,這世上最難降的妖魔是什么冠桃? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮道宅,結(jié)果婚禮上食听,老公的妹妹穿的比我還像新娘。我一直安慰自己污茵,他們只是感情好樱报,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著泞当,像睡著了一般迹蛤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上襟士,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天盗飒,我揣著相機(jī)與錄音,去河邊找鬼陋桂。 笑死逆趣,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嗜历。 我是一名探鬼主播宣渗,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼秸脱!你這毒婦竟也來了落包?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤摊唇,失蹤者是張志新(化名)和其女友劉穎咐蝇,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡有序,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年抹腿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旭寿。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡警绩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盅称,到底是詐尸還是另有隱情肩祥,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布缩膝,位于F島的核電站混狠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏疾层。R本人自食惡果不足惜将饺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望痛黎。 院中可真熱鬧予弧,春花似錦、人聲如沸湖饱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琉历。三九已至坠七,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旗笔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工拄踪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝇恶,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓惶桐,卻偏偏與公主長得像撮弧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子姚糊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

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

  • 樹的概述 樹是一種非常常用的數(shù)據(jù)結(jié)構(gòu)贿衍,樹與前面介紹的線性表,棧救恨,隊(duì)列等線性結(jié)構(gòu)不同贸辈,樹是一種非線性結(jié)構(gòu) 1.樹的定...
    Jack921閱讀 4,455評論 1 31
  • 一些概念 數(shù)據(jù)結(jié)構(gòu)就是研究數(shù)據(jù)的邏輯結(jié)構(gòu)和物理結(jié)構(gòu)以及它們之間相互關(guān)系,并對這種結(jié)構(gòu)定義相應(yīng)的運(yùn)算肠槽,而且確保經(jīng)過這...
    Winterfell_Z閱讀 5,802評論 0 13
  • 1 樹 1.1 定義 每個節(jié)點(diǎn)可以有多個子節(jié)點(diǎn)桩盲,而該節(jié)點(diǎn)則是相應(yīng)子節(jié)點(diǎn)的父節(jié)點(diǎn)。但是每個節(jié)點(diǎn)只能有一個父節(jié)點(diǎn)(只有...
    櫻木天亥閱讀 8,495評論 1 11
  • 現(xiàn)在才吃今天的第一餐席吴、早上起床并不晚赌结、磨磨唧唧還給自己化了個妝、胡亂的吃了一點(diǎn)面包牛奶孝冒、等我出門的時候已經(jīng)11點(diǎn)了...
    Snow吉閱讀 191評論 0 0
  • 哈哈哈柬姚,看完了,你是不是也想馬上開始寫日記呢迈倍? 是的伤靠,日記,是親子溝通的最美好的工具之一啼染,日記宴合,是表達(dá)感恩最深情的...
    雅軒1號閱讀 889評論 2 0