二叉查找樹

二叉查找樹定義

二叉查找樹(Binary Search Tree), 也稱為二叉搜索樹, 有序二叉樹(ordered binary tree), 排序二叉樹(sorted binary tree), 是指一顆空樹或具有以下性質(zhì)的二叉樹:
1. 任意節(jié)點的左子樹不空, 則左子樹上所有節(jié)點的值均小于它的根節(jié)點的值
2. 任意節(jié)點的右子樹不空, 則右子樹上所有節(jié)點均大于它的根節(jié)點的值
3. 任意節(jié)點的左,右子樹也分別為二叉查找樹
4. 沒有鍵值相等的節(jié)點

二叉查找樹通常采用二叉鏈表作為二叉查找樹的存儲結(jié)構(gòu), 相比于其他數(shù)據(jù)結(jié)構(gòu)的優(yōu)勢在于查找,插入時間復(fù)雜度較低, 為O(log n). 它是基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu), 用于構(gòu)建 紅黑數(shù), B-Tree, B+Tree, B*Tree等.

二叉查找樹的Java實現(xiàn)
1. 二叉查找樹節(jié)點定義
public class BSTNode<T extends Comparable<T>> {
    T key;                // 關(guān)鍵字(鍵值)
    BSTNode<T> left;    // 左孩子
    BSTNode<T> right;    // 右孩子
    BSTNode<T> parent;    // 父結(jié)點

    public BSTNode(T key, BSTNode<T> parent, BSTNode<T> left, BSTNode<T> right) {
        this.key = key;
        this.parent = parent;
        this.left = left;
        this.right = right;
    }

    public T getKey() {
        return key;
    }

    public String toString() {
        return "key:"+key;
    }
}

BSTNode是二叉查找樹的內(nèi)部節(jié)點, 它包含以下信息:

  1. key 對二叉樹進(jìn)行排序的依據(jù)
  2. left 左邊子節(jié)點
  3. right 右邊子節(jié)點
  4. parent 當(dāng)前節(jié)點的父節(jié)點
2. 二叉查找樹的查找
/*
 * (遞歸實現(xiàn))查找"二叉樹x"中鍵值為key的節(jié)點
 */
private BSTNode<T> search(BSTNode<T> x, T key) {
    if (x==null)
        return x;

    int cmp = key.compareTo(x.key);
    if (cmp < 0)
        return search(x.left, key);
    else if (cmp > 0)
        return search(x.right, key);
    else
        return x;
}

public BSTNode<T> search(T key) {
    return search(mRoot, key);
}

search 的過程比較簡單, 從根節(jié)點開始, 根據(jù)給定的key與節(jié)點的key值進(jìn)行比較, 直到 "cmp" = 0 或節(jié)點x = 0.

3. 查找最大值, 最小值

最大值

/*
 * 查找最大結(jié)點:返回tree為根結(jié)點的二叉樹的最大結(jié)點。
 */
private BSTNode<T> maximum(BSTNode<T> tree) {
    if (tree == null)
        return null;

    while(tree.right != null)
        tree = tree.right;
    return tree;
}

public T maximum() {
    BSTNode<T> p = maximum(mRoot);
    if (p != null)
        return p.key;

    return null;
}

最小值

/*
 * 查找最小結(jié)點:返回tree為根結(jié)點的二叉樹的最小結(jié)點。
 */
private BSTNode<T> minimum(BSTNode<T> tree) {
    if (tree == null)
        return null;

    while(tree.left != null)
        tree = tree.left;
    return tree;
}

public T minimum() {
    BSTNode<T> p = minimum(mRoot);
    if (p != null)
        return p.key;

    return null;
}
4. 查找前繼節(jié)點(predecessor) 后繼節(jié)點(successor)

查找前繼節(jié)點

/*
 * 找結(jié)點(x)的前繼結(jié)點。即,查找"二叉樹中數(shù)據(jù)值小于該結(jié)點"的"最大結(jié)點"。
 */
public BSTNode<T> predecessor(BSTNode<T> x) {
    // 如果x存在左孩子,則"x的前繼結(jié)點"為 "以其左孩子為根的子樹的最大結(jié)點"。
    if (x.left != null)
        return maximum(x.left);

    // 如果x沒有左孩子。則x由分以下兩種情況討論:
    // (01) x是"一個右孩子",則"x的前驅(qū)結(jié)點"為 "它的父結(jié)點"。
    // (01) x是"一個左孩子",則查找"x的最低的父結(jié)點,并且x在該父結(jié)點右子樹上孩子或是其又子節(jié)點",找到的這個"最低的父結(jié)點"就是"x的前繼結(jié)點"。
    BSTNode<T> y = x.parent;
    while ((y!=null) && (x==y.left)) {
        x = y;
        y = y.parent;
    }

    return y;
}

查找后繼節(jié)點

/*
 * 找結(jié)點(x)的后繼結(jié)點。即,查找"二叉樹中數(shù)據(jù)值大于該結(jié)點"的"最小結(jié)點"。
 */
public BSTNode<T> successor(BSTNode<T> x) {
    // 如果x存在右孩子,則"x的后繼結(jié)點"為 "以其右孩子為根的子樹的最小結(jié)點"。
    if (x.right != null)
        return minimum(x.right);

    // 如果x沒有右孩子。則x分以下兩種情況進(jìn)行討論:
    // (01) x是"一個左孩子",則"x的后繼結(jié)點"為 "它的父結(jié)點"。
    // (02) x是"一個右孩子",則查找"x的最低的父結(jié)點,并且x是該父結(jié)點左子樹上的孩子節(jié)點或直接是左子節(jié)點",找到的這個"最低的父結(jié)點"就是"x的后繼結(jié)點"。
    BSTNode<T> y = x.parent;
    while ((y!=null) && (x==y.right)) {
        x = y;
        y = y.parent;
    }

    return y;
}
5. 插入節(jié)點

/*
 * 將結(jié)點插入到二叉樹中
 *
 * 參數(shù)說明:
 *     tree 二叉樹的
 *     z 插入的結(jié)點
 */
private void insert(BSTree<T> bst, BSTNode<T> z) {
    int cmp;
    BSTNode<T> y = null;
    BSTNode<T> x = bst.mRoot;

    // 1. 查找z的插入位置
    while (x != null) {
        y = x;
        cmp = z.key.compareTo(x.key);
        if (cmp < 0)
            x = x.left;
        else
            x = x.right;
    }

    //2. 根據(jù)位置決定放在root節(jié)點, y的左/右邊
    z.parent = y;
    if (y==null) // y是null
        bst.mRoot = z;
    else {
        cmp = z.key.compareTo(y.key);
        if (cmp < 0)
            y.left = z;
        else
            y.right = z;
    }
}

/*
 * 新建結(jié)點(key),并將其插入到二叉樹中
 *
 * 參數(shù)說明:
 *     tree 二叉樹的根結(jié)點
 *     key 插入結(jié)點的鍵值
 */
public void insert(T key) {
    BSTNode<T> z=new BSTNode<T>(key,null,null,null);

    // 如果新建結(jié)點失敗,則返回。
    if (z != null)
        insert(this, z);
}
5. 刪除節(jié)點
/*
 * 刪除結(jié)點(z),并返回被刪除的結(jié)點
 *
 * 參數(shù)說明:
 *     bst 二叉樹
 *     z 刪除的結(jié)點
 */
private BSTNode<T> remove(BSTree<T> bst, BSTNode<T> z) {
    BSTNode<T> x=null;
    BSTNode<T> y=null;
    /** 1. 待刪除節(jié)點情況
     * 1) 節(jié)點z最多一個子節(jié)點, 則刪除z后將對應(yīng)子節(jié)點代替原來的位置
     * 2) 節(jié)點有兩個子節(jié)點, 調(diào)用 successor方法獲取其后繼節(jié)點, 后繼節(jié)點代替原來的那個節(jié)點
     */
    if ((z.left == null) || (z.right == null) )
        y = z; // 節(jié)點z最多有一個子節(jié)點
    else{
        // 這里有個知識點, 既然y是后繼節(jié)點, 則 y 必定沒有兩個子節(jié)點
        y = successor(z); // 節(jié)點z有兩個子節(jié)點, 則調(diào)用 successor 查詢z對應(yīng)的子節(jié)點
    }

    // 2. 將待刪除節(jié)點的子節(jié)點賦值給x
    if (y.left != null)
        x = y.left;
    else
        x = y.right;
    /**
     * x 情況
     * 1. x是被刪除節(jié)點的子節(jié)點
     * 2. x是被刪除節(jié)點的后繼節(jié)點的子節(jié)點
     */
    if (x != null) {
        x.parent = y.parent;
    }

    // y.parent == null, 則說明y是mRoot節(jié)點
    if (y.parent == null)
        bst.mRoot = x;
    else if (y == y.parent.left)
        y.parent.left = x;
    else
        y.parent.right = x;

    // 若y是z的后繼節(jié)點
    if (y != z) {
        z.key = y.key;
    }

    return y;
}
6. 二叉查找樹完整實現(xiàn)代碼

二叉查找樹Java實現(xiàn) BSTree.java

總結(jié)

二叉查找樹總體實現(xiàn)比較簡單, 但這是學(xué)習(xí) RBTree, B-Tree, B+Tree 等數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,076評論 2 375

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