二叉搜索樹

關(guān)于二叉樹中的點到底用“節(jié)點”還是“結(jié)點”。節(jié)點被認(rèn)為是一個有處理能力的實體,比如網(wǎng)絡(luò)上的一臺計算機(jī);而結(jié)點則只是一個交叉點,像“結(jié)繩記事”,打個結(jié)做個標(biāo)記,僅此而已。還有就是,要記?。?strong>一般算法中點的都是結(jié)點。

二叉搜索樹支持多種操作,包括Search、Minimum、Maximum、Predecessor、Successor、Insert和Delete等操作,且其基本操作所花費的時間與這棵樹的高度成正比。

完全二叉樹的最壞運(yùn)行時間為:O({\bf log}\ n),樹如果由n個節(jié)點的組成的線性鏈,則最好運(yùn)行時間為:O(n)。

二叉搜索樹的定義

二叉搜索樹又稱為做二叉排序樹、二叉查找樹。其要么是一課空樹,要么是一個滿足以下性質(zhì)的二叉樹:

  1. 若它的左子樹不空,則左子樹上所有節(jié)點的關(guān)鍵字均小于根節(jié)點關(guān)鍵字
  2. 若它的右子樹不空,則右子樹上所有節(jié)點的關(guān)鍵字均大于根節(jié)點關(guān)鍵字
  3. 它的左右子樹依舊是二叉搜索樹
  4. 沒右關(guān)鍵字相等的節(jié)點

算法導(dǎo)論中指出,左右子樹均可以包含和根節(jié)點大小相等的元素

二叉搜索樹具有的特點:

  1. 按中序遍歷二叉搜索樹所得的中序序列是一個遞增的有序序列。
  2. 統(tǒng)一個數(shù)據(jù)集合可構(gòu)造的二叉搜索樹不唯一,但中序序列相同。

Search

二叉搜索樹在搜索某個關(guān)鍵字遇到節(jié)點x時,將關(guān)鍵字kx.key比較:

  1. 如果k==x.key,查找終止并返回x。
  2. 如果k<x.key,繼續(xù)在x的左子樹中查找,如果左子樹為空則返回null。
  3. 如果k>x.key,繼續(xù)在x的右子樹中查找,如果左子樹為空則返回null。

上述過程所需的運(yùn)行時間為O(h),其中h表示樹的高度。其代碼為:

public TreeNode search(TreeNode node, int key) {
    while (node != null) {
        if (node.val == key) return node;
        else if (node.val > key) node = node.left;
        else node = node.right;
    }
    return node;
}

Minimum

二叉搜索樹從根節(jié)點沿著左孩子的指針直到遇到null,并返回左孩子指針為null的節(jié)點。

上述過程所需的運(yùn)行時間為O(h),其中h表示樹的高度。其代碼為:

public TreeNode minimum(TreeNode node) {
    while (node.left != null) node = node.left;
    return node;
}

Maximum

二叉搜索樹從根節(jié)點沿著右孩子的指針直到遇到null,并返回右孩子指針為null的節(jié)點。

上述過程所需的運(yùn)行時間為O(h),其中h表示樹的高度。其代碼為:

public TreeNode maximum(TreeNode node) {
    while (node.right != null) node = node.right;
    return node;
}

Successor

二叉搜索樹尋找后繼分為兩種情況:

  1. 節(jié)點x的右子樹非空,那么x的后繼就是x右子樹的最左節(jié)點(右子樹中最小的節(jié)點)。
  2. 節(jié)點x的右子樹為空,那么簡單地從x沿著樹向上搜索,直到搜索到當(dāng)前根節(jié)點是父節(jié)點的左子樹的根為止,此時該父節(jié)點是x的后繼。

上述過程所需的運(yùn)行時間為O(h),其中h表示樹的高度。其代碼為:

public TreeNode successor(TreeNode node) {
    if (node.right != null) return minimum(node.right);
    while (node.p != null && node == node.p.right) node = node.p;
    return node.p;
}

Predecessor

二叉搜索樹尋找前驅(qū)分為兩種情況:

  1. 節(jié)點x的左子樹非空,那么x的前驅(qū)就是x左子樹的最右節(jié)點(左子樹中最大的節(jié)點)。
  2. 節(jié)點x的右子樹為空,那么簡單地從x沿著樹向上搜索,直到搜索到當(dāng)前根節(jié)點是父節(jié)點的右子樹的根為止,此時該父節(jié)點是x的前驅(qū)

上述過程所需的運(yùn)行時間為O(h),其中h表示樹的高度。其代碼為:

public TreeNode predecessor(TreeNode node) {
    if (node.left != null) return maximum(node.left);
    while (node.p != null && node == node.p.left) node = node.p;
    return node.p;
}

Insert

插入節(jié)點之后仍要滿足二叉搜索樹的特性。如果要插入的值為z,二叉搜索樹在節(jié)點x處的處理情況分為兩種:

  1. 如果當(dāng)前節(jié)點為空,直接將z值插入;
  2. 如果當(dāng)前節(jié)點不空:
    1. 如果z<x.key,繼續(xù)在x的左子樹中插入
    2. 如果z>x.key,繼續(xù)在x的右子樹中插入

其代碼為:

public TreeNode insert(TreeNode root, int z) {
    if (root == null) {
        root = new TreeNode(z);
        return root;
    }
    TreeNode node = root;
    while (true) {
        if (z < node.val) {
            if (node.left == null) {
                node.left = new TreeNode(z);
                node.left.p = node;
                break;
            }
            node = node.left;
        }
        if (z > node.val) {
            if (node.right == null) {
                node.right = new TreeNode(z);
                node.right.p = node;
                break;
            }
            node = node.right;
        }
    }
    return root;
}

Delete

刪除節(jié)點之后仍要滿足二叉搜索樹的特性。在二叉搜索樹T中刪除一個節(jié)點z分為三種情況:

  1. 如果節(jié)點z沒有孩子節(jié)點,則直接刪除該節(jié)點
  2. 如果節(jié)點z只有左子樹或者右子樹中的一個,則直接繼承:將該子樹移到被刪除節(jié)點的位置
  3. 如果節(jié)點z擁有兩個子樹,則用后繼或者先驅(qū)節(jié)點取代被刪除的節(jié)點。

其代碼為:

public TreeNode delete(TreeNode node, int x) {
    TreeNode root = node;
    while (node != null) {
        if (node.val == x) {
            if (node.left != null && node.right != null) { // 情況 3
                TreeNode maximum = maximum(node.left);
                node.val = maximum.val;
                if (maximum.p.left != null && maximum.p.left.val == maximum.val) maximum.p.left = maximum.left;
                if (maximum.p.right != null && maximum.p.right.val == maximum.val) maximum.p.right = null;
            } else if (node.left == null && node.right == null) { // 情況 1
                if (node.p.left != null && node.p.left.val == x) node.p.left = null;
                if (node.p.right != null && node.p.right.val == x) node.p.right = null;
            } else { // 情況 2
                if (node.left != null) {
                    node.val = node.left.val;
                    node.right = node.left.right;
                    if (node.left.left != null) node.left.left.p = node;
                    if (node.left.right != null) node.left.right.p = node;
                    node.left = node.left.left;
                } else if (node.right != null) {
                    node.val = node.right.val;
                    node.left = node.right.left;
                    if (node.right.left != null) node.right.left.p = node;
                    if (node.right.right != null) node.right.right.p = node;
                    node.right = node.right.right;
                }
            }
            break;
        }
        if (node.val > x && node.left != null) node = node.left;
        if (node.val < x && node.right != null) node = node.right;
    }
    return root;
}

算法導(dǎo)論第三版中給出了構(gòu)建深度接近O({\bf log}\ n)的隨機(jī)構(gòu)建二叉搜索樹,按照隨機(jī)的次序插入關(guān)鍵字到一顆空樹中。為降低樹的高度還出現(xiàn)了各種“平衡"搜索樹。

完整代碼:

package org.example;

public class Template {
    public TreeNode search(TreeNode node, int key) {
        while (node != null) {
            if (node.val == key) return node;
            else if (node.val > key) node = node.left;
            else node = node.right;
        }
        return node;
    }

    public TreeNode minimum(TreeNode node) {
        while (node.left != null) node = node.left;
        return node;
    }

    public TreeNode maximum(TreeNode node) {
        while (node.right != null) node = node.right;
        return node;
    }

    public TreeNode successor(TreeNode node) {
        if (node.right != null) return minimum(node.right);
        while (node.p != null && node == node.p.right) node = node.p;
        return node.p;
    }

    public TreeNode predecessor(TreeNode node) {
        if (node.left != null) return maximum(node.left);
        while (node.p != null && node == node.p.left) node = node.p;
        return node.p;
    }

    public TreeNode insert(TreeNode root, int z) {
        if (root == null) {
            root = new TreeNode(z);
            return root;
        }
        TreeNode node = root;
        while (true) {
            if (z < node.val) {
                if (node.left == null) {
                    node.left = new TreeNode(z);
                    node.left.p = node;
                    break;
                }
                node = node.left;
            }
            if (z > node.val) {
                if (node.right == null) {
                    node.right = new TreeNode(z);
                    node.right.p = node;
                    break;
                }
                node = node.right;
            }
        }
        return root;
    }

    public TreeNode delete(TreeNode node, int x) {
        TreeNode root = node;
        while (node != null) {
            if (node.val == x) {
                if (node.left != null && node.right != null) {
                    TreeNode maximum = maximum(node.left);
                    node.val = maximum.val;
                    if (maximum.p.left != null && maximum.p.left.val == maximum.val) maximum.p.left = maximum.left;
                    if (maximum.p.right != null && maximum.p.right.val == maximum.val) maximum.p.right = null;
                } else if (node.left == null && node.right == null) {
                    if (node.p.left != null && node.p.left.val == x) node.p.left = null;
                    if (node.p.right != null && node.p.right.val == x) node.p.right = null;
                } else {
                    if (node.left != null) {
                        node.val = node.left.val;
                        node.right = node.left.right;
                        if (node.left.left != null) node.left.left.p = node;
                        if (node.left.right != null) node.left.right.p = node;
                        node.left = node.left.left;
                    } else if (node.right != null) {
                        node.val = node.right.val;
                        node.left = node.right.left;
                        if (node.right.left != null) node.right.left.p = node;
                        if (node.right.right != null) node.right.right.p = node;
                        node.right = node.right.right;
                    }
                }
                break;
            }
            if (node.val > x && node.left != null) node = node.left;
            if (node.val < x && node.right != null) node = node.right;
        }
        return root;
    }

    public static void main(String[] args) {
        Template template = new Template();

        TreeNode left = new Template().new TreeNode(3);
        TreeNode right = new Template().new TreeNode(6);
        TreeNode root = new Template().new TreeNode(4, left, right);
        left.p = root;
        right.p = root;

        System.out.println(template.search(root, 5));
        System.out.println(template.minimum(root).val);
        System.out.println(template.maximum(root).val);
        TreeNode successor = template.successor(root);
        System.out.println(successor == null ? null : successor.val);
        TreeNode predecessor = template.predecessor(right);
        System.out.println(predecessor == null ? null : predecessor.val);

        TreeNode node = template.insert(root, 1);
        System.out.println(node.left.left.p.val);

        TreeNode node2 = template.insert(root, 0);
        TreeNode node3 = template.insert(root, 2);
        TreeNode node1 = template.delete(root, 1);
        System.out.println(node1.left.left.right.val);

    }

    class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode p;

        TreeNode() {
        }

        TreeNode(int val) {
            this.val = val;
        }

        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }

    }
}

題目鏈接

  1. 450. 刪除二叉搜索樹中的節(jié)點

參考文獻(xiàn):

  1. 二叉搜索樹
  2. 二叉搜索樹及其操作詳解
  3. Thomas,H.Cormen,Charles,E.Leiserson,Ronald,L.Rivest,Clifford,Stein,殷建平,徐云,王剛,劉曉光,蘇明,鄒恒明,王宏志. 算法導(dǎo)論(原書第3版)[J]. 計算機(jī)教育, 2013(10):1.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 概述 二叉搜索樹,顧名思義,其主要目的用于搜索,它是二叉樹結(jié)構(gòu)中最基本的一種數(shù)據(jù)結(jié)構(gòu),是后續(xù)理解B樹、B+樹、...
    CodingTech閱讀 3,201評論 5 35
  • 二叉搜索樹 顧名思義,一棵二叉搜索樹是以一棵二叉樹來組織的。如下圖所示,這樣一棵樹可以使用一個鏈表數(shù)據(jù)結(jié)構(gòu)來表示,...
    Longshihua閱讀 800評論 0 1
  • 1、前言 二叉樹是非常重要的一種數(shù)據(jù)結(jié)構(gòu),二叉搜索樹是其中的一種類型,其任意節(jié)點x,左子樹中的關(guān)鍵字最大不超過x....
    某昆閱讀 677評論 0 4
  • BST簡介 查詢BST 插入和刪除 #1. BST簡介 BST(Binary Search Tree),二叉搜索樹...
    MrDecoder閱讀 811評論 0 4
  • 二叉搜索樹結(jié)構(gòu) 鏈表數(shù)據(jù)結(jié)構(gòu)存儲 包含key,衛(wèi)星數(shù)據(jù),左右子節(jié)點,父節(jié)點 左子樹的最大值小于根節(jié)點的值小于右子樹...
    火樂君_52cd閱讀 262評論 0 0

友情鏈接更多精彩內(nèi)容