首頁 > 軟體

java基礎二元搜尋樹圖文詳解

2022-03-10 16:00:46

概念

二元搜尋樹又稱二叉排序樹,它或者是一棵空樹,或者是具有以下性質的二元樹:
1、若它的左子樹不為空,則左子樹上所有節點的值都小於根結點的值。
2、若它的右子樹不為空,則右子樹上所有節點的值都大於根結點的值。
3、它的左右子樹也分別為二元搜尋樹

直接實踐

準備工作:定義一個樹節點的類,和二元搜尋樹的類。

搜尋二元樹的查詢功能

假設我們已經構造好了一個這樣的二元樹,如下圖

我們要思考的第一個問題是如何查詢某個值是否在該二元樹中?

根據上述的邏輯,我們來把搜尋的方法進行完善。

搜尋二元樹的插入操作

根據上述邏輯,我們來寫一個插入節點的程式碼。

搜尋二元樹 刪除節點的操作 - 難點

再來分析一下:curDummy 和 parentDummy 是怎麼找到“替罪羊”的。

總程式 - 模擬實現二元搜尋樹

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


public class BinarySearchTree {
    TreeNode root;

    //在二元樹中 尋找指定 val 值的節點
    // 找到了,返回其節點地址;沒找到返回 null
    public TreeNode search(int key){
        TreeNode cur = this.root;
        while(cur != null){
            if(cur.val == key){
                return cur;
            }else if(cur.val < key){
                cur = cur.right;
            }else{
                cur = cur.left;
            }
        }
        return null;
    }
    // 插入操作
    public boolean insert(int key){
        if(this.root == null){
            this.root = new TreeNode(key);
            return true;
        }
        TreeNode cur = this.root;
        TreeNode parent = null;
        while(cur!=null){
            if(key > cur.val){
                parent  = cur;
                cur = cur.right;
            }else if(cur.val == key){
                return false;
            }else{
                parent  = cur;
                cur = cur.left;
            }
        }
        TreeNode node = new TreeNode(key);
        if(parent .val > key){
            parent.left = node;
        }else{
            parent.right = node;
        }
        return true;
    }
    // 刪除操作
    public void remove(int key){
        TreeNode cur = root;
        TreeNode parent = null;
        // 尋找 刪除節點位置。
        while(cur!=null){
            if(cur.val == key){
                removeNode(cur,parent);// 真正刪除節點的程式碼
                break;
            }else if(cur.val < key){
                parent = cur;
                cur = cur.right;
            }else{
                parent = cur;
                cur = cur.left;
            }
        }
    }
    // 輔助刪除方法:真正刪除節點的程式碼
    private void removeNode(TreeNode cur,TreeNode parent){
        // 情況一
        if(cur.left == null){
            if(cur == this.root){
                this.root = this.root.right;
            }else if( cur == parent.left){
                parent.left = cur.right;
            }else{
                parent.right = cur.right;
            }
            // 情況二
        }else if(cur.right == null){
            if(cur == this.root){
                this.root = root.left;
            }else if(cur == parent.left){
                parent.left = cur.left;
            }else{
                parent.right = cur.left;
            }
            // 情況三
        }else{
            // 第二種方法:在刪除節點的右子樹中尋找最小值,
            TreeNode parentDummy = cur;
            TreeNode curDummy = cur.right;
            while(curDummy.left != null){
                parentDummy = curDummy;
                curDummy = curDummy.left;
            }
            // 此時 curDummy 指向的 cur 右子樹
            cur.val = curDummy.val;
            if(parentDummy.left != curDummy){
                parentDummy.right = curDummy.right;
            }else{
                parentDummy.left = curDummy.right;
            }

        }
    }
   // 中序遍歷
    public void inorder(TreeNode root){
        if(root == null){
            return;
        }
        inorder(root.left);
        System.out.print(root.val+" ");
        inorder(root.right);
    }

    public static void main(String[] args) {
        int[] array = {10,8,19,3,9,4,7};
        BinarySearchTree binarySearchTree = new BinarySearchTree();
        for (int i = 0; i < array.length; i++) {
            binarySearchTree.insert(array[i]);
        }
        binarySearchTree.inorder(binarySearchTree.root);
        System.out.println();// 換行
        System.out.print("插入重複的資料 9:" + binarySearchTree.insert(9));
        System.out.println();// 換行
        System.out.print("插入不重複的資料 1:" + binarySearchTree.insert(1));
        System.out.println();// 換行
        binarySearchTree.inorder(binarySearchTree.root);
        System.out.println();// 換行
        binarySearchTree.remove(19);
        System.out.print("刪除元素 19 :");
        binarySearchTree.inorder(binarySearchTree.root);
        System.out.println();// 換行
        System.out.print("查詢不存在的資料50 :");
        System.out.println(binarySearchTree.search(50));
        System.out.print("查詢存在的資料 7:");
        System.out.println(binarySearchTree.search(7));
    }
}

效能分析

  插入和刪除操作都必須先查詢,查詢效率代表了二元搜尋樹中各個操作的效能。

  對有n個結點的二元搜尋樹,若每個元素查詢的概率相等,則二元搜尋樹平均查詢長度是結點在二元搜尋樹的深度的函數,即結點越深,則比較次數越多。

  但對於同一個關鍵碼集合,如果各關鍵碼插入的次序不同,可能得到不同結構的二元搜尋樹:

如果我們能保證 二元搜尋樹的左右子樹高度差不超過1。儘量滿足高度平衡條件。
這就成 AVL 樹了(高度平衡的二元搜尋樹)。而AVL樹,也有缺點:需要一個頻繁的旋轉。浪費很多效率。
至此 紅黑樹就誕生了,避免更多的旋轉。

和 java 類集的關係

TreeMap 和 TreeSet 即 java 中利用搜尋樹實現的 Map 和 Set;實際上用的是紅黑樹,而紅黑樹是一棵近似平衡的二元搜尋樹,即在二元搜尋樹的基礎之上 + 顏色以及紅黑樹性質驗證,關於紅黑樹的內容,等博主學了,會寫部落格的。

總結 

到此這篇關於java基礎二元搜尋樹的文章就介紹到這了,更多相關java二元搜尋樹內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com