1. 程式人生 > >二叉搜尋樹的插入、刪除、查詢等操作:Java語言實現

二叉搜尋樹的插入、刪除、查詢等操作:Java語言實現

1 二叉搜尋樹介紹

 二叉搜尋樹(BST, Binary Search Tree),也稱二叉排序樹或二叉查詢樹。

二叉搜尋樹:一棵二叉樹,可以為空;如果不為空,滿足以下性質:
1. 非空左子樹的所有鍵值小於其根結點的鍵值。
2. 非空右子樹的所有鍵值大於其根結點的鍵值。

3. 左、右子樹都是二叉搜尋樹。

2 二叉搜尋樹的主要操作

2.1 二叉搜尋樹的查詢操作 查詢從根結點開始,如果樹為空,返回NULL

 若搜尋樹非空,則根結點關鍵字和X進行比較, 並進行不同處理:
 若X小於根結點鍵值,只需在左子樹中繼續搜尋;
 如果X大於根結點的鍵值, 在右子樹中進行繼續搜尋;

 若兩者比較結果是相等,搜尋完成,返回指向此結點的指標。

//查詢元素:遞迴方法
    BinarySearchTreeNode find(BinarySearchTreeNode root, int data) {
        if (root == null)
            return null;//查詢失敗
        if (data < root.getData())
            return find(root.getLeft(),data);//在左子樹中繼續查詢
        else if (data > root.getData())
            return find(root.getRight(),data);//在右子樹中繼續查詢
        else
            return root;//查詢成功, 返回找到的結點的地址
    }

    //查詢元素:非遞迴方法
    BinarySearchTreeNode iterFind(BinarySearchTreeNode root, int data) {
        while (root != null) {
            if (data < root.getData())
                root = root.getLeft();//向左子樹中移動, 繼續查詢
            else if (data > root.getData())
                root = root.getRight();//向右子樹中移動, 繼續查詢
            else
                return root;//查詢成功, 返回找到的結點的地址
        }
        return null;//查詢失敗
    }

2.2 查詢最大和最小元素

 最大元素一定是在樹的最右分枝的端結點上
 最小元素一定是在樹的最左分枝的端結點上

 最小元素一定是在樹的最左分枝的端結點上
//查詢最小元素:遞迴方法 (最小元素一定是在樹的最左分枝的端結點上)
    BinarySearchTreeNode findMin(BinarySearchTreeNode root) {
        if (root == null)
            return null;//空的二叉搜尋樹,返回null
        else if (root.getLeft() == null)
            return root;//找到最左葉結點並返回
        else
            return findMin(root.getLeft());//沿左分支繼續查詢
    }

    //查詢最小元素:非遞迴方法 (最小元素一定是在樹的最左分枝的端結點上)
    BinarySearchTreeNode iterFindMin(BinarySearchTreeNode root) {
        if (root == null)
            return null;//空的二叉搜尋樹,返回null
        while (root.getLeft() != null)
            root = root.getLeft();//沿左分支繼續查詢
        return root;//找到最左葉結點並返回
    }

    //查詢最大元素:遞迴方法 (最大元素一定是在樹的最右分枝的端結點上)
    BinarySearchTreeNode findMax(BinarySearchTreeNode root) {
        if (root == null)
            return null;//空的二叉搜尋樹,返回null
        else if (root.getRight() == null)
            return root;//找到最右葉結點並返回
        else
            return findMax(root.getRight());//沿右分支繼續查詢
    }

    //查詢最大元素:非遞迴方法 (最大元素一定是在樹的最右分枝的端結點上)
    BinarySearchTreeNode iterFindMax(BinarySearchTreeNode root) {
        if (root == null)
            return null;//空的二叉搜尋樹,返回null
        while (root.getRight() != null)
            root = root.getRight();//沿右分支繼續查詢
        return root;//找到最右葉結點並返回
    }
2.3 二叉搜尋樹的插入

       將元素X插入二叉搜尋樹BST中,關鍵是要找到元素應該插入的位置。位置的確定可以利用與查詢函式Find類似的方法,如果在樹BST中找到X,說明要插入的元素已存在,可放棄插入操作。如果沒找到X,查詢終止的位置就是X應插入的位置。

//插入元素
    BinarySearchTreeNode insert(BinarySearchTreeNode root, int data) {
        if (root == null)
            root = new BinarySearchTreeNode(data);//若原樹為空, 生成並返回一個結點的二叉搜尋樹
        else {
            if (data < root.getData())     //開始找要插入元素的位置
                root.setLeft(insert(root.getLeft(),data));//遞迴插入左子樹
            else if (data > root.getData())
                root.setRight(insert(root.getRight(),data));//遞迴插入右子樹
        }
        return root;
    }

2.4 二叉搜尋樹的刪除

 考慮三種情況:

 要刪除的是葉結點: 直接刪除, 並再修改其父結點指標---置為null 


  要刪除的結點只有一個孩子結點: 將其父結點的指標指向要刪除結點的孩子結點


 要刪除的結點有左、右兩棵子樹:用另一結點替代被刪除結點: 右子樹的最小元素 或者 左子樹的最大元素

//刪除元素
    BinarySearchTreeNode delete(BinarySearchTreeNode root, int data) {
        BinarySearchTreeNode temp;
        if (root == null)
            System.out.println("要刪除的元素未找到");
        else if (data < root.getData())
            root.setLeft(delete(root.getLeft(),data));//左子樹遞迴刪除
        else if (data > root.getData())
            root.setRight(delete(root.getRight(),data));//右子樹遞迴刪除
        else {    //找到要刪除的結點
            if (root.getLeft() != null && root.getRight() != null) { //被刪除結點有左右兩個子結點
                temp = findMin(root.getRight());//在右子樹中找最小的元素填充刪除結點
                root.setData(temp.getData());
                root.setRight(delete(root.getRight(),root.getData()));//在刪除結點的右子樹中刪除最小元素
            }else {   //被刪除結點有一個或無子結點
                temp = root;
                if (root.getLeft() == null)  //有右孩子或無子結點
                    root = root.getRight();
                else if (root.getRight() == null) //有左孩子或無子結點
                    root = root.getLeft();
                temp = null;
            }
        }
        return root;
    }

2.5 二叉搜尋樹的遍歷

       需要注意的是:中序遍歷二叉搜尋樹時,將得到一個有序表。

//中序遍歷:遞迴方法  (中序遍歷二叉搜尋樹時,將得到一個有序表)
    void InOrderRecursive(BinarySearchTreeNode root) {
        if(root == null)
            return;
        InOrderRecursive(root.getLeft());//中序遍歷其左子樹
        System.out.print(root.getData() + " ");//訪問根結點
        InOrderRecursive(root.getRight());//中序遍歷其右子樹
    } 

3 Java整體程式碼實現

3.1 建立二叉搜尋樹結點類

package Binary_Tree_Study;

/**
 * Created by Administrator on 2018/5/20.
 */
public class BinarySearchTreeNode {
    private int data;//結點的資料
    private BinarySearchTreeNode left;//指向左孩子結點
    private BinarySearchTreeNode right;//指向左孩子結點

    public BinarySearchTreeNode(int data) {
        this.data = data;
        this.left = null;
        this.right =null;
    }

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public BinarySearchTreeNode getLeft() {
        return left;
    }

    public void setLeft(BinarySearchTreeNode left) {
        this.left = left;
    }

    public BinarySearchTreeNode getRight() {
        return right;
    }

    public void setRight(BinarySearchTreeNode right) {
        this.right = right;
    }
}

3.2 二叉搜尋樹的上述操作程式碼實現

package Binary_Tree_Study;

/**
 * Created by Administrator on 2018/5/20.
 */
public class BinarySearchTree {
    private BinarySearchTreeNode root;//根結點

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

    //查詢元素:遞迴方法
    BinarySearchTreeNode find(BinarySearchTreeNode root, int data) {
        if (root == null)
            return null;//查詢失敗
        if (data < root.getData())
            return find(root.getLeft(),data);//在左子樹中繼續查詢
        else if (data > root.getData())
            return find(root.getRight(),data);//在右子樹中繼續查詢
        else
            return root;//查詢成功, 返回找到的結點的地址
    }

    //查詢元素:非遞迴方法
    BinarySearchTreeNode iterFind(BinarySearchTreeNode root, int data) {
        while (root != null) {
            if (data < root.getData())
                root = root.getLeft();//向左子樹中移動, 繼續查詢
            else if (data > root.getData())
                root = root.getRight();//向右子樹中移動, 繼續查詢
            else
                return root;//查詢成功, 返回找到的結點的地址
        }
        return null;//查詢失敗
    }

    //查詢最小元素:遞迴方法 (最小元素一定是在樹的最左分枝的端結點上)
    BinarySearchTreeNode findMin(BinarySearchTreeNode root) {
        if (root == null)
            return null;//空的二叉搜尋樹,返回null
        else if (root.getLeft() == null)
            return root;//找到最左葉結點並返回
        else
            return findMin(root.getLeft());//沿左分支繼續查詢
    }

    //查詢最小元素:非遞迴方法 (最小元素一定是在樹的最左分枝的端結點上)
    BinarySearchTreeNode iterFindMin(BinarySearchTreeNode root) {
        if (root == null)
            return null;//空的二叉搜尋樹,返回null
        while (root.getLeft() != null)
            root = root.getLeft();//沿左分支繼續查詢
        return root;//找到最左葉結點並返回
    }

    //查詢最大元素:遞迴方法 (最大元素一定是在樹的最右分枝的端結點上)
    BinarySearchTreeNode findMax(BinarySearchTreeNode root) {
        if (root == null)
            return null;//空的二叉搜尋樹,返回null
        else if (root.getRight() == null)
            return root;//找到最右葉結點並返回
        else
            return findMax(root.getRight());//沿右分支繼續查詢
    }

    //查詢最大元素:非遞迴方法 (最大元素一定是在樹的最右分枝的端結點上)
    BinarySearchTreeNode iterFindMax(BinarySearchTreeNode root) {
        if (root == null)
            return null;//空的二叉搜尋樹,返回null
        while (root.getRight() != null)
            root = root.getRight();//沿右分支繼續查詢
        return root;//找到最右葉結點並返回
    }

    //插入元素
    BinarySearchTreeNode insert(BinarySearchTreeNode root, int data) {
        if (root == null)
            root = new BinarySearchTreeNode(data);//若原樹為空, 生成並返回一個結點的二叉搜尋樹
        else {
            if (data < root.getData())     //開始找要插入元素的位置
                root.setLeft(insert(root.getLeft(),data));//遞迴插入左子樹
            else if (data > root.getData())
                root.setRight(insert(root.getRight(),data));//遞迴插入右子樹
        }
        return root;
    }

    //刪除元素
    BinarySearchTreeNode delete(BinarySearchTreeNode root, int data) {
        BinarySearchTreeNode temp;
        if (root == null)
            System.out.println("要刪除的元素未找到");
        else if (data < root.getData())
            root.setLeft(delete(root.getLeft(),data));//左子樹遞迴刪除
        else if (data > root.getData())
            root.setRight(delete(root.getRight(),data));//右子樹遞迴刪除
        else {    //找到要刪除的結點
            if (root.getLeft() != null && root.getRight() != null) { //被刪除結點有左右兩個子結點
                temp = findMin(root.getRight());//在右子樹中找最小的元素填充刪除結點
                root.setData(temp.getData());
                root.setRight(delete(root.getRight(),root.getData()));//在刪除結點的右子樹中刪除最小元素
            }else {   //被刪除結點有一個或無子結點
                temp = root;
                if (root.getLeft() == null)  //有右孩子或無子結點
                    root = root.getRight();
                else if (root.getRight() == null) //有左孩子或無子結點
                    root = root.getLeft();
                temp = null;
            }
        }
        return root;
    }

    //中序遍歷:遞迴方法  (中序遍歷二叉搜尋樹時,將得到一個有序表)
    void InOrderRecursive(BinarySearchTreeNode root) {
        if(root == null)
            return;
        InOrderRecursive(root.getLeft());//中序遍歷其左子樹
        System.out.print(root.getData() + " ");//訪問根結點
        InOrderRecursive(root.getRight());//中序遍歷其右子樹
    }

    //以下函式呼叫相應的方法
    public void callFindMin() {
        BinarySearchTreeNode cur = findMin(root);
        System.out.println("\n最小元素為:" + cur.getData());
    }

    public void callIterFindMin() {
        BinarySearchTreeNode cur = iterFindMin(root);
        System.out.println("\n最小元素為:" + cur.getData());
    }

    public void callFindMax() {
        BinarySearchTreeNode cur = findMax(root);
        System.out.println("\n最大元素為:" + cur.getData());
    }

    public void callIterFindMax() {
        BinarySearchTreeNode cur = iterFindMax(root);
        System.out.println("\n最大元素為:" + cur.getData());
    }

    public void callInsert(int data) {
        root = insert(root, data);
    }

    public void callDelete(int data) {
        root = delete(root, data);
    }

    public void callInOrderRecursive() {
        InOrderRecursive(root);
    }
}

3.3 測試

package Binary_Tree_Study;

/**
 * Created by Administrator on 2018/5/20.
 */
public class BinarySearchTreeTest {
    public static void main(String[] args) {
        BinarySearchTree tree = new BinarySearchTree();
        /* 建立如下的二叉搜尋樹
              5
           /     \
          3       7
         /  \    /  \
        2   4   6   8 */
        tree.callInsert(5);
        tree.callInsert(3);
        tree.callInsert(2);
        tree.callInsert(4);
        tree.callInsert(7);
        tree.callInsert(6);
        tree.callInsert(8);
        System.out.println("中序遍歷為:");
        tree.callInOrderRecursive();//2 3 4 5 6 7 8
        tree.callFindMin();//最小元素為:2
        tree.callFindMax();//最大元素為:8
        System.out.println("\nD刪除結點資料 2");
        tree.callDelete(2);
        System.out.println("刪除刪除結點資料2後的中序遍歷為:");
        tree.callInOrderRecursive();//3 4 5 6 7 8
        tree.callIterFindMin();//最小元素為:3
        tree.callIterFindMax();//最大元素為:8
    }
}

4 參考資料