圖解:二叉搜索樹算法(BST)

摘要: 原創出處:www.bysocket.com 泥瓦匠BYSocket 但願轉載,保留摘要,謝謝! 「歲月極美,在於它必然的流逝」 「春花 秋月 夏日 冬雪」 — 三毛java

1、樹 & 二叉樹

是由節點和邊構成,儲存元素的集合。節點分根節點、父節點和子節點的概念。 如圖:樹深=4; 5是根節點;一樣8與3的關係是父子節點關係。node

1

二叉樹binary tree,則加了「二叉」(binary),意思是在樹中做區分。每一個節點至多有兩個子(child),left child & right child。二叉樹在不少例子中使用,好比二叉樹表示算術表達式。 如圖:1/8是左節點;2/3是右節點; 2git

2、二叉搜索樹 BST

顧名思義,二叉樹上又加了個搜索的限制。其要求:每一個節點比其左子樹元素大,比其右子樹元素小。 如圖:每一個節點比它左子樹的任意節點大,並且比它右子樹的任意節點小github

3

3、BST Java實現

直接上代碼,對應代碼分享在 Github 主頁 BinarySearchTree.java算法

package org.algorithm.tree;
/*
 * Copyright [2015] [Jeff Lee]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * 二叉搜索樹(BST)實現
 *
 * Created by bysocket on 16/7/7.
 */
public class BinarySearchTree {
    /**
     * 根節點
     */
    public static TreeNode root;

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

    /**
     * 查找
     *      樹深(N) O(lgN)
     *      1. 從root節點開始
     *      2. 比當前節點值小,則找其左節點
     *      3. 比當前節點值大,則找其右節點
     *      4. 與當前節點值相等,查找到返回TRUE
     *      5. 查找完畢未找到,
     * @param key
     * @return
     */
    public TreeNode search (int key) {
        TreeNode current = root;
        while (current != null
                && key != current.value) {
            if (key < current.value )
                current = current.left;
            else
                current = current.right;
        }
        return current;
    }

    /**
     * 插入
     *      1. 從root節點開始
     *      2. 若是root爲空,root爲插入值
     *      循環:
     *      3. 若是當前節點值大於插入值,找左節點
     *      4. 若是當前節點值小於插入值,找右節點
     * @param key
     * @return
     */
    public TreeNode insert (int key) {
        // 新增節點
        TreeNode newNode = new TreeNode(key);
        // 當前節點
        TreeNode current = root;
        // 上個節點
        TreeNode parent  = null;
        // 若是根節點爲空
        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;
                }
            }
        }
    }

    /**
     * 刪除節點
     *      1.找到刪除節點
     *      2.若是刪除節點左節點爲空 , 右節點也爲空;
     *      3.若是刪除節點只有一個子節點 右節點 或者 左節點
     *      4.若是刪除節點左右子節點都不爲空
     * @param key
     * @return
     */
    public TreeNode delete (int key) {
        TreeNode parent  = root;
        TreeNode current = root;
        boolean isLeftChild = false;
        // 找到刪除節點 及 是否在左子樹
        while (current.value != key) {
            parent = current;
            if (current.value > key) {
                isLeftChild = true;
                current = current.left;
            } else {
                isLeftChild = false;
                current = current.right;
            }

            if (current == null) {
                return current;
            }
        }

        // 若是刪除節點左節點爲空 , 右節點也爲空
        if (current.left == null && current.right == null) {
            if (current == root) {
                root = null;
            }
            // 在左子樹
            if (isLeftChild == true) {
                parent.left = null;
            } else {
                parent.right = null;
            }
        }
        // 若是刪除節點只有一個子節點 右節點 或者 左節點
        else if (current.right == null) {
            if (current == root) {
                root = current.left;
            } else if (isLeftChild) {
                parent.left = current.left;
            } else {
                parent.right = current.left;
            }

        }
        else if (current.left == null) {
            if (current == root) {
                root = current.right;
            } else if (isLeftChild) {
                parent.left = current.right;
            } else {
                parent.right = current.right;
            }
        }
        // 若是刪除節點左右子節點都不爲空
        else if (current.left != null && current.right != null) {
            // 找到刪除節點的後繼者
            TreeNode successor = getDeleteSuccessor(current);
            if (current == root) {
                root = successor;
            } else if (isLeftChild) {
                parent.left = successor;
            } else {
                parent.right = successor;
            }
            successor.left = current.left;
        }
        return current;
    }

    /**
     * 獲取刪除節點的後繼者
     *      刪除節點的後繼者是在其右節點樹種最小的節點
     * @param deleteNode
     * @return
     */
    public TreeNode getDeleteSuccessor(TreeNode deleteNode) {
        // 後繼者
        TreeNode successor = null;
        TreeNode successorParent = null;
        TreeNode 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 void toString(TreeNode root) {
        if (root != null) {
            toString(root.left);
            System.out.print("value = " + root.value + " -> ");
            toString(root.right);
        }
    }
}

/**
 * 節點
 */
class TreeNode {

    /**
     * 節點值
     */
    int value;

    /**
     * 左節點
     */
    TreeNode left;

    /**
     * 右節點
     */
    TreeNode right;

    public TreeNode(int value) {
        this.value = value;
        left  = null;
        right = null;
    }
}

1. 節點數據結構 首先定義了節點的數據接口,節點分左節點和右節點及自己節點值。如圖express

4

代碼以下:apache

/**
 * 節點
 */
class TreeNode {

    /**
     * 節點值
     */
    int value;

    /**
     * 左節點
     */
    TreeNode left;

    /**
     * 右節點
     */
    TreeNode right;

    public TreeNode(int value) {
        this.value = value;
        left  = null;
        right = null;
    }
}

  2. 插入 插入,和刪除同樣會引發二叉搜索樹的動態變化。插入相對刪處理邏輯相對簡單些。如圖插入的邏輯:數據結構

5

a. 從root節點開始 b.若是root爲空,root爲插入值 c.循環: d.若是當前節點值大於插入值,找左節點 e.若是當前節點值小於插入值,找右節點 代碼對應:app

/**
     * 插入
     *      1. 從root節點開始
     *      2. 若是root爲空,root爲插入值
     *      循環:
     *      3. 若是當前節點值大於插入值,找左節點
     *      4. 若是當前節點值小於插入值,找右節點
     * @param key
     * @return
     */
    public TreeNode insert (int key) {
        // 新增節點
        TreeNode newNode = new TreeNode(key);
        // 當前節點
        TreeNode current = root;
        // 上個節點
        TreeNode parent  = null;
        // 若是根節點爲空
        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;
                }
            }
        }
    }

  3.查找 其算法複雜度 : O(lgN),樹深(N)。如圖查找邏輯:less

6

a.從root節點開始 b.比當前節點值小,則找其左節點 c.比當前節點值大,則找其右節點 d.與當前節點值相等,查找到返回TRUE e.查找完畢未找到 代碼對應:

/**
     * 查找
     *      樹深(N) O(lgN)
     *      1. 從root節點開始
     *      2. 比當前節點值小,則找其左節點
     *      3. 比當前節點值大,則找其右節點
     *      4. 與當前節點值相等,查找到返回TRUE
     *      5. 查找完畢未找到,
     * @param key
     * @return
     */
    public TreeNode search (int key) {
        TreeNode current = root;
        while (current != null
                && key != current.value) {
            if (key < current.value )
                current = current.left;
            else
                current = current.right;
        }
        return current;
    }

4. 刪除 首先找到刪除節點,其尋找方法:刪除節點的後繼者是在其右節點樹種最小的節點。如圖刪除對應邏輯:7

結果爲: 8

a.找到刪除節點 b.若是刪除節點左節點爲空 , 右節點也爲空; c.若是刪除節點只有一個子節點 右節點 或者 左節點 d.若是刪除節點左右子節點都不爲空 代碼對應見上面完整代碼。   案例測試代碼以下,BinarySearchTreeTest.java

package org.algorithm.tree;
/*
 * Copyright [2015] [Jeff Lee]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * 二叉搜索樹(BST)測試案例 {@link BinarySearchTree}
 *
 * Created by bysocket on 16/7/10.
 */
public class BinarySearchTreeTest {

    public static void main(String[] args) {
        BinarySearchTree b = new BinarySearchTree();
        b.insert(3);b.insert(8);b.insert(1);b.insert(4);b.insert(6);
        b.insert(2);b.insert(10);b.insert(9);b.insert(20);b.insert(25);

        // 打印二叉樹
        b.toString(b.root);
        System.out.println();

        // 是否存在節點值10
        TreeNode node01 = b.search(10);
        System.out.println("是否存在節點值爲10 => " + node01.value);
        // 是否存在節點值11
        TreeNode node02 = b.search(11);
        System.out.println("是否存在節點值爲11 => " + node02);

        // 刪除節點8
        TreeNode node03 = b.delete(8);
        System.out.println("刪除節點8 => " + node03.value);
        b.toString(b.root);


    }
}

運行結果以下:

value = 1 -> value = 2 -> value = 3 -> value = 4 -> value = 6 -> value = 8 -> value = 9 -> value = 10 -> value = 20 -> value = 25 -> 
是否存在節點值爲10 => 10
是否存在節點值爲11 => null
刪除節點8 => 8
value = 1 -> value = 2 -> value = 3 -> value = 4 -> value = 6 -> value = 9 -> value = 10 -> value = 20 -> value = 25 ->

4、小結

與偶爾吃一碗「老壇酸菜牛肉麪」同樣的味道,品味一個算法,好比BST,的時候,老是那種說不出的味道。 樹,二叉樹的概念 BST算法 相關代碼分享在 Github 主頁

如以上文章或連接對你有幫助的話,別忘了在文章結尾處評論哈~ 你也能夠點擊頁面右邊「分享」懸浮按鈕哦,讓更多的人閱讀這篇文章。

相關文章
相關標籤/搜索