1. 程式人生 > >二叉排序樹(Binary Sort Tree)查詢、插入、刪除 Java實現

二叉排序樹(Binary Sort Tree)查詢、插入、刪除 Java實現

順序儲存的插入和刪除可以通過在表末端插入和刪除元素同最後一個元素互換來保持高效率,但是無序造成查詢的效率很低。

如果查詢的資料表是有序線性表,並且是順序儲存的,查詢可以折半、插值、斐波那契等查詢演算法來實現,但是因為有序在插入和刪除操作上,就需要耗費大量時間。

二叉排序樹可以使得插入和刪除的效率不錯,又可以比較高效率地實現查詢。

二叉排序樹(Binary Sort Tree),又稱為二叉查詢樹。它或者是一棵空樹,或者是具有下列性質的二叉樹

1.若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;

2.若它的右子樹不空,則右子樹上所有結點的值均小於它的根結點的值;

3.它的左、右子樹也分別為二叉排序樹。

二叉排序樹的查詢有遞迴和非遞迴兩種方法

public class BinarySortTree {
    
    /**
     * 根結點
     */
    private TreeNode rootNode;
    
    /**
     * 獲取rootNode
     * @return
     */
    public TreeNode getRootNode() {
        return this.rootNode;
    }
    
    /**
     * 建立二叉排序樹 的第一種方法(通過insertBST建立)
     * @param array
     * @return
     */
    public void createBinarySortTree1(int[] array) {
        
        for (int i = 0; i <array.length; i++) {
            if (!insertBST(array[i])) {
                System.out.println("已存在" + array[i] + ",不再插入二叉排序樹");
            }
        }
        
    }
    
    /**
     * 建立二叉排序樹 的第二種方法
     * @param array
     * @return
     */
    public void createBinarySortTree2(int[] array) {
        
        for (int i = 0; i < array.length; i++) {
            if (i == 0) {
                rootNode = new TreeNode(array[i]);
                continue;
            }
            
            TreeNode node = rootNode;
            while (true) {
                if (array[i] < node.getData()) {
                    
                    if (node.getLchild() == null) {
                        node.setLchild(new TreeNode(array[i]));
                        break;
                    } else {
                        node = node.getLchild();
                    }
                    
                } else if (array[i] == node.getData()) {
                    System.out.println("已存在" + array[i] + ",不再插入二叉排序樹");
                    break;
                } else {
                    
                    if (node.getRchild() == null) {
                        node.setRchild(new TreeNode(array[i]));
                        break;
                    } else {
                        node = node.getRchild();
                    }
                    
                }
            }
        }
        
    }
    
    /**
     * 中序遍歷
     * @param node
     */
    public void inOrderTraverse(TreeNode node) {
        if (node != null) {
            inOrderTraverse(node.getLchild());
            System.out.print(node.getData() + " ");
            inOrderTraverse(node.getRchild());
        }
    }
    
    /**
     * 二叉排序樹的查詢 (遞迴實現)
     * 
     * 查詢可以有兩種實現方法,一種是遞迴,一種是while 迴圈。
     * 在插入和刪除操作中也首先需要查詢操作,這時使用while 迴圈比較好,可以知道要查詢結點的雙親結點
     * @param node
     * @param key
     * @return 返回
     */
    public boolean searchBST(TreeNode node, int key) {
        if (node == null) {
            return false;
        } else if (key == node.getData()) {
            return true;
        } else if (key < node.getData()) {
            return searchBST(node.getLchild(), key);
        } else {
            return searchBST(node.getRchild(), key);
        }
    }
    
    /**
     * 二叉排序樹的插入
     * @param node
     * @param key
     */
    public boolean insertBST(int key) {
        
        //相當於 search 用非遞迴實現 -----begin 
        TreeNode node = rootNode;
        //查詢 最後訪問的 node
        TreeNode searchLastNode = null;
        
        while (node != null) {
            searchLastNode = node;
            if (key < node.getData()) {
                node = node.getLchild();
            } else if (key > node.getData()) {
                node = node.getRchild();
            } else {
                return false;
            }
        }
        // search ----end
        
        TreeNode newNode = new TreeNode(key);
        
        if (rootNode == null) {
            rootNode = newNode;
        } else if (key < searchLastNode.getData()) {
            searchLastNode.setLchild(newNode);
        } else {
            searchLastNode.setRchild(newNode);
        }
        
        return true;
    }
    
    
    public boolean deleteBST(int key) {
        //相當於 search 用非遞迴實現 -----begin 
        TreeNode node = rootNode;
        //儲存 其雙親結點,如果是根節點,則雙親結點為null
        TreeNode parentNode = null;
        
        while (node != null) {
            if (key < node.getData()) {
                parentNode = node;
                node = node.getLchild();
            } else if (key > node.getData()) {
                parentNode = node;
                node = node.getRchild();
            } else {
                //相等時已找到
                break ;
            }
        }
        
        // search ----end
        
        //沒有找到要刪除的node
        if (node == null) {
            return false;
        }
        
        // 右子樹為空 ,重接左子樹
        if (node.getRchild() == null) {
            if (parentNode != null) {
                if (parentNode.getLchild() == node)
                    parentNode.setLchild(node.getLchild());
                else
                    parentNode.setRchild(node.getLchild());
            } else {
                rootNode = node.getLchild();
            }
        // 左子樹為空,重接右子樹
        } else if (node.getLchild() == null) {
            if (parentNode != null) {
                if (parentNode.getLchild() == node)
                    parentNode.setLchild(node.getRchild());
                else
                    parentNode.setRchild(node.getRchild());
            } else {
                rootNode = node.getRchild();
            }
        // 左右子樹都不為空 ,可以將 要刪除結點 按中序遍歷的前驅或後繼 替代 要刪除結點的位置,此處取前驅
        } else {
            
            //取前驅結點 ,先轉左,然後一直到最右
            TreeNode replaceNode = node.getLchild();
            TreeNode replaceParentNode = node;
            
            if (replaceNode.getRchild() != null) {
                replaceParentNode = replaceNode;
                replaceNode = replaceNode.getRchild();
            }
            //獲取到前驅結點極其雙親結點
            
            //如果前驅結點的雙親結點是 要刪除的結點
            if (replaceParentNode == node)
                replaceParentNode.setLchild(replaceNode.getLchild());
            else 
                replaceParentNode.setRchild(replaceNode.getLchild());
            
            node.setData(replaceNode.getData());
        }
        
        
        return true;
    }
    
    public static void main(String[] args) {
//        int[] array = new int[15];
//        System.out.print("隨機數:");
//        for (int i = 0; i < 15; i++) {
//            array[i] = (int) (Math.random() * 100);
//            System.out.print(array[i] + " ");
//        }
        
        int[] array = {4, 10, 1, 13, 3, 15, 9, 11, 8, 5, 14, 2, 6, 7, 12};
        
        BinarySortTree binarySortTree = new BinarySortTree();
        //建立二叉排序樹
        binarySortTree.createBinarySortTree1(array);
        
        //中序遍歷 即是 從小到大的排序
        System.out.print("\n中序遍歷結果:");
        binarySortTree.inOrderTraverse(binarySortTree.getRootNode());
        
        //二叉排序樹的查詢
        boolean searchResult = binarySortTree.searchBST(binarySortTree.getRootNode(), 2);
        if (searchResult) 
            System.out.println("\n查到");
        else
            System.out.println("\n查不到");
        
        //二叉排序樹的刪除 
        binarySortTree.deleteBST(2);
        binarySortTree.deleteBST(1);
        binarySortTree.deleteBST(3);
        binarySortTree.deleteBST(4);
        binarySortTree.deleteBST(10);
        binarySortTree.inOrderTraverse(binarySortTree.getRootNode());
        
    }

}

/**
 * 二叉樹結點
 * 
 *
 */
class TreeNode {
    private int data;

    private TreeNode lchild;

    private TreeNode rchild;
    
    public TreeNode(int data) {
        this.data = data;
    }
    
    public int getData() {
        return data;
    }

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

    public TreeNode getLchild() {
        return lchild;
    }

    public void setLchild(TreeNode lchild) {
        this.lchild = lchild;
    }

    public TreeNode getRchild() {
        return rchild;
    }

    public void setRchild(TreeNode rchild) {
        this.rchild = rchild;
    }
    
    @Override
    public String toString() {
        return "TreeNode [data=" + data + ", lchild=" + lchild + ", rchild=" + rchild + "]";
    }

}

程式碼所示二叉樹


執行結果: