樹結(jié)構(gòu)——二叉查找樹

定義

二叉查找樹,也稱二叉搜索樹、有序二叉樹(英語(yǔ):ordered binary tree),排序二叉樹(英語(yǔ):sorted binary tree),是指一棵空樹或者具有下列性質(zhì)的二叉樹:

  1. 若任意節(jié)點(diǎn)的左子樹不空,則左子樹上所有結(jié)點(diǎn)的值均小于它的根結(jié)點(diǎn)的值;
  • 若任意節(jié)點(diǎn)的右子樹不空,則右子樹上所有結(jié)點(diǎn)的值均大于它的根結(jié)點(diǎn)的值;
  • 任意節(jié)點(diǎn)的左、右子樹也分別為二叉查找樹;
  • 沒(méi)有鍵值相等的節(jié)點(diǎn)。
刪除一個(gè)有左、右子樹的節(jié)點(diǎn)

先弄出一個(gè)二叉樹的葉子

    private static class BinaryNode<K,V> {
        K key; // 鍵
        V value; // 值
        BinaryNode<K,V> left; // 左子樹
        BinaryNode<K,V> right; // 右子樹

        BinaryNode(K key,V value) {
            this(key,value, null, null);
        }

        BinaryNode(K key,V value, BinaryNode<K,V> left, BinaryNode<K,V> right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }

插入算法

向一個(gè)二元搜尋樹b中插入一個(gè)節(jié)點(diǎn)s的算法,過(guò)程為:

  1. 若b是空樹,則將s所指結(jié)點(diǎn)作為根節(jié)點(diǎn)插入,否則:
  • 若s->data等于b的根節(jié)點(diǎn)的數(shù)據(jù)域之值,則返回,否則:
  • 若s->data小于b的根節(jié)點(diǎn)的數(shù)據(jù)域之值,則把s所指節(jié)點(diǎn)插入到左子樹中,否則:
  • 把s所指節(jié)點(diǎn)插入到右子樹中。(新插入節(jié)點(diǎn)總是葉子節(jié)點(diǎn))
    private BinaryNode insert(K key,V value,BinaryNode<K,V> node){
        // 如果根為空,則直接把傳進(jìn)來(lái)的建值作為樹的根
        if(node == null){
            return new BinaryNode(key,value);
        }
        // 比較樹根和將要成為葉子的值
        int result = compare(key,node.key);
        // 節(jié)點(diǎn)值比根小,將節(jié)點(diǎn)插入左子樹,否則插入右子樹
        if(result<0){
            node.left = insert(key,value,node.left);
        }else if(result > 0){
            node.right = insert(key,value,node.right);
        }
        return node;
    }
    final int compare(Object k1, Object k2) {
        return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
                : comparator.compare((K)k1, (K)k2);
    }

查找算法

在二元搜尋樹b中查找x的過(guò)程為:

  1. 若b是空樹,則搜索失敗,否則:
  • 若x等于b的根節(jié)點(diǎn)的數(shù)據(jù)域之值,則查找成功;否則:
  • 若x小于b的根節(jié)點(diǎn)的數(shù)據(jù)域之值,則搜索左子樹;否則:
  • 查找右子樹。
    private BinaryNode<K, V> getEntry(K key) {
        if(key == null){
            throw new NullPointerException();
        }
        BinarySearchTree.BinaryNode<K,V> p = rootTree;
        while (p != null){
            // 比較樹根和和傳入的key
            int cmp = compare(key,p.key);
            // 節(jié)點(diǎn)值比根小,則查找左子樹,否則查找右子樹
            if(cmp < 0)
                p = p.left;
            else if (cmp > 0)
                p = p.right;
            else
                return p;
        }
        return null;
    }

刪除算法

  1. 若*p結(jié)點(diǎn)為葉子結(jié)點(diǎn),即PL(左子樹)和PR(右子樹)均為空樹。由于刪去葉子結(jié)點(diǎn)不破壞整棵樹的結(jié)構(gòu),則只需修改其雙親結(jié)點(diǎn)的指針即可。
  • p結(jié)點(diǎn)只有左子樹PL或右子樹PR,此時(shí)只要令PL或PR直接成為其雙親結(jié)點(diǎn)f的左子樹(當(dāng)p是左子樹)或右子樹(當(dāng)p是右子樹)即可,作此修改也不破壞二叉查找樹的特性。
  • p結(jié)點(diǎn)的左子樹和右子樹均不空。在刪去p之后,為保持其它元素之間的相對(duì)位置不變,可按中序遍歷保持有序進(jìn)行調(diào)整,可以有兩種做法:其一是令p的左子樹為f的左/右(依p是f的左子樹還是右子樹而定)子樹,s為p左子樹的最右下的結(jié)點(diǎn),而p的右子樹為s的右子樹;其二是令p的直接前驅(qū)(in-order predecessor)或直接后繼(in-order successor)替代p,然后再?gòu)亩娌檎覙渲袆h去它的直接前驅(qū)(或直接后繼)。
    private BinaryNode<K, V> deleteEntry(K key, BinaryNode<K, V> node) {
        if (node == null) {
            return node;
        }
        int result = compare(key, node.key);
        if (result < 0) {
            // 存在左子樹
            node.left = deleteEntry(key, node.left);
        } else if (result > 0) {
            // 存在右子樹
            node.right = deleteEntry(key, node.right);
        } else if (node.left != null && node.right != null) {
             /**
             * 這邊刪除可以有兩種方式,一種是找到右子樹的最左節(jié)點(diǎn),還有一種是找到左子樹的最右節(jié)點(diǎn)
             * 然后把要?jiǎng)h除的節(jié)點(diǎn)替換掉
             * node.key = findMax(node.left).key;
             * node.value = findMax(node.left).value;
             * node.left = deleteEntry(node.key, node.left);
             */
            // 找到右子樹的左邊最小節(jié)點(diǎn)把要?jiǎng)h除的節(jié)點(diǎn)替換掉
            node.key = findMin(node.right).key;
            node.value = findMin(node.right).value;
            // 替換掉之后將節(jié)點(diǎn)刪除
            node.right = deleteEntry(node.key, node.right);
        } else
            node = (node.left != null) ? node.left : node.right;
        return node;

    }

二叉查找樹的弊端

最壞情況下,當(dāng)先后插入的關(guān)鍵字有序時(shí),構(gòu)成的二叉查找樹蛻變?yōu)閱沃洌@個(gè)時(shí)候復(fù)雜度會(huì)退化成O(n)


完整代碼

/**
 * Created by tianzeng on 2017/5/13.
 * 二叉查找樹
 * 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. 沒(méi)有鍵值相等的節(jié)點(diǎn)
 */

  public class BinarySearchTree<K, V> {
    /**
     * 節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)
     */
    private static class BinaryNode<K, V> {
        K key;
        V value;
        BinaryNode<K, V> left;
        BinaryNode<K, V> right;

        BinaryNode(K key, V value) {
            this(key, value, null, null);
        }

        BinaryNode(K key, V value, BinaryNode<K, V> left, BinaryNode<K, V> right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }

    private BinaryNode rootTree; //
    private final Comparator<? super K> comparator;

    /**
     * 構(gòu)造一個(gè)空的二叉查找樹
     */
    public BinarySearchTree() {
        this.comparator = null;
        rootTree = null;
    }
    public BinarySearchTree(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }
    /**
     * 清空二叉查找樹
     */
    public void clear() {
        rootTree = null;
    }

    /**
     * 判斷二叉樹是否為空
     */
    public boolean isEmpty() {
        return rootTree == null;
    }

    final int compare(Object k1, Object k2) {
        return comparator == null ? ((Comparable<? super K>) k1).compareTo((K) k2)
                : comparator.compare((K) k1, (K) k2);
    }

    /**
     * 插入元素
     */
    public void put(K key, V value) {
        rootTree = insert(key, value, rootTree);
    }

    /**
     * 插入元素
     */
    private BinaryNode insert(K key, V value, BinaryNode<K, V> node) {
        // 如果根為空
        if (node == null) {
            return new BinaryNode(key, value);
        }
        int result = compare(key, node.key);
        // 節(jié)點(diǎn)值比根小,左子樹
        if (result < 0) {
            node.left = insert(key, value, node.left);
        } else if (result > 0) {
            node.right = insert(key, value, node.right);
        }
        return node;
    }

    /**
     * 查找元素
     */
    public V get(K key) {
        BinarySearchTree.BinaryNode<K, V> p = getEntry(key);
        return (p == null ? null : p.value);
    }

    private BinaryNode<K, V> getEntry(K key) {
        if (key == null) {
            throw new NullPointerException();
        }
        BinarySearchTree.BinaryNode<K, V> p = rootTree;
        while (p != null) {
            int cmp = compare(key, p.key);
            if (cmp < 0)
                p = p.left;
            else if (cmp > 0)
                p = p.right;
            else
                return p;
        }
        return null;
    }

    public void remove(K key) {
        rootTree = deleteEntry(key, rootTree);
    }

    /**
     * 尋找該節(jié)點(diǎn)的最小節(jié)點(diǎn)
     */
    BinaryNode<K,V> findMin(BinaryNode<K, V> node){
        if(node == null){
            return null;
        }else if(node.left == null){
            return node;
        }
        return findMin(node.left);
    }
    BinaryNode<K,V> findMax(BinaryNode<K, V> node){
        if(node == null){
            return null;
        }else if(node.right == null){
            return node;
        }
        return findMax(node.right);
    }
    private BinaryNode<K, V> deleteEntry(K key, BinaryNode<K, V> node) {
        if (node == null) {
            return node;
        }
        int result = compare(key, node.key);
        if (result < 0) {
            // 存在左子樹
            node.left = deleteEntry(key, node.left);
        } else if (result > 0) {
            // 存在右子樹
            node.right = deleteEntry(key, node.right);
        } else if (node.left != null && node.right != null) {
            /**
             * node.key = findMax(node.left).key;
             * node.value = findMax(node.left).value;
             * node.left = deleteEntry(node.key, node.left);
             */
            // 找到右子樹的左邊最小節(jié)點(diǎn)把要?jiǎng)h除的節(jié)點(diǎn)替換掉
            node.key = findMin(node.right).key;
            node.value = findMin(node.right).value;
            // 替換掉之后將節(jié)點(diǎn)刪除
            node.right = deleteEntry(node.key, node.right);
        } else
            node = (node.left != null) ? node.left : node.right;
        return node;

    }
    public void print(){
        print(rootTree);
    }

    public void print(BinaryNode root) {
        if (root == null) {
            return;
        }

        List<BinaryNode> list = new LinkedList<>();
        BinaryNode node;
        // 當(dāng)前層的結(jié)點(diǎn)個(gè)數(shù)
        int current = 1;
        // 記錄下一層的結(jié)點(diǎn)個(gè)數(shù)
        int next = 0;
        list.add(root);

        while (list.size() > 0) {
            node = list.remove(0);
            current--;
            System.out.printf("%-3s", node.value);

            if (node.left != null) {
                list.add(node.left);
                next++;
            }
            if (node.right != null) {
                list.add(node.right);
                next++;
            }

            if (current ==0) {
                System.out.println();
                current = next;
                next = 0;
            }
        }
    }
}

測(cè)試:

參考資料

維基百科——二叉搜索樹
二叉樹(BST樹)內(nèi)結(jié)點(diǎn)的刪除
6天通吃樹結(jié)構(gòu)—— 第一天 二叉查找樹

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

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