1. 程式人生 > >數據結構 - 從二叉搜索樹說到AVL樹(一)之二叉搜索樹的操作與詳解(Java)

數據結構 - 從二叉搜索樹說到AVL樹(一)之二叉搜索樹的操作與詳解(Java)

判斷 right 不為 exist avl 輸入 位置 bubuko get

  二叉搜索樹(Binary Search Tree),簡稱BST,顧名思義,一顆可以用於搜索的二叉樹。BST在數據結構中占有很重要的地位,一些高級樹結構都是其的變種,例如AVL樹、紅黑樹等,因此理解BST對於後續樹結構的學習有很好的作用。同時利用BST可以進行排序,稱為二叉排序,也是很重要的一種思想。

  二叉樹的性質:任意一個節點的所有左子樹元素都比該元素值要小,而所有右子樹元素都比該元素值要大。

  符合該性質的二叉樹就是一顆二叉搜索樹,當然前提下是樹中不允許有重復元素。

  所有的二叉搜索樹的中序遍歷序列都是一個順序序列, 一下的幾種都是二叉搜索樹

  技術分享圖片    技術分享圖片      技術分享圖片

  接下來講二叉搜索樹的三個主要操作:查找,增加,刪除。

  一、查找

    二叉搜索樹本來就是用於查找數據的一種結構,主要步驟是:

      ① 選擇根節點作為當前節點

      ② 如果當前節點為null,則返回false表示找不到該元素

      ③ 如果當前節點不為null,判斷當前節點是否與所搜索元素相等,如相等,則返回true表示能找到該元素

      ④ 如果當前節點的值比所搜索值小,則選擇當前節點的左節點作為當前節點,比搜索值大則選擇右節點作為當前節點,然後從②循環過程

  二、增加

    二叉搜索樹增加節點重點是根據查找元素的過程找到該節點的插入位置:

      ① 如果根節點為空,則當前插入(增加)的節點為二叉樹的根,如根節點不為空,設置根節點為當前節點

      ② 如果當前節點的值與插入節點的值相等,返回false表示插入失敗,節點值已存在於樹中

      ② 如插入節點的值比當前節點的值小,且當前節點的左子樹為空,則把插入節點增加到當前節點的左子樹,設置插入節點的父節點為當前節點並返回true表示插入成功,如左子樹不為空,設置 當前節點的左子樹為當前節點。如果插入節點的值比當前節點的值大,且當前節點的右子樹為空,則把插入節點增加到當前節點的右子樹,設置插入節點的父節點為當前節點並返回true,如右子樹不為空,設置當前節點的右子樹為當前節點。從②循環過程

    具體代碼如下:

    public boolean insert(int
elem) { TreeNode node = new TreeNode(elem); if (null == this.root) { this.root = node; return true; } else { return insertChild(this.root, node); } } /** * insert the newNode to the child position of parent node * @param parent * @param newNode * @return true if the newNode insert successfully or false if the newNode have been exist */ private boolean insertChild(TreeNode parent, TreeNode newNode) { if (parent.getElem() == newNode.getElem()) { return false; } else if (parent.getElem() > newNode.getElem()) { if (null == parent.getLeft()) { parent.setLeft(newNode); newNode.setParent(parent); return true; } else { return insertChild(parent.getLeft(), newNode); } } else { if (null == parent.getRight()) { parent.setRight(newNode); newNode.setParent(parent); return true; } else { return insertChild(parent.getRight(), newNode); } } }

  三、刪除

    二叉搜索樹的刪除操作重點在於刪除這個節點之後對其他節點的重組,根據刪除節點位置又分為以下幾種情況:

      情況1 刪除節點為葉子節點:把父節點對應的子樹設為空

      情況2 刪除節點只有左子樹或只有右子樹:把父節點對應的子樹指向刪除節點的左子樹或者右子樹,並把左子樹或右子樹的父節點指向刪除節點的父節點

      情況3 刪除節點的左子樹和右子樹均不為空:找到刪除節點的前驅節點,把前驅節點的值代替刪除節點的值,其他不做任何操作,把這個前驅節點作為刪除節點,則該情況轉換成情況1或者情況2

      比如想刪除節點 5

      技術分享圖片 >>> 技術分享圖片 >>> 技術分享圖片

    這樣就換成了刪除原來的節點4,變成了情況1,刪除掉原來的節點4之後,剩下的依然是一顆二叉搜索樹。

    具體代碼:

    /**
     * delete the node which element equals to parameter elem
     * @param elem
     * @return true if the node can be found and delete otherwise false
     */
    public boolean delete(int elem) {
        if (null == this.root) {
            return false;
        } else {
            TreeNode node = this.root;
            // find out the node need to be deleted
            while (null != node) {
                if (node.getElem() == elem) {
                    deleteNode(node);
                    return true;
                } else if (node.getElem() > elem) {
                    node = node.getLeft();
                } else {
                    node = node.getRight();
                }
            }
            return false;
        }
    }
    private void deleteNode(TreeNode node) {
        if (null == node.getLeft() && null == node.getRight()) {
            // deleted node is a leave
            if (null  == node.getParent()) {
                // deleted node is root
                this.root = null;
            } else if (node == node.getParent().getLeft()) {
                // deleted node is the left child of its parent
                node.getParent().setLeft(null);
            } else {
                // deleted node is the right child of its parent
                node.getParent().setRight(null);
            }
        } else if (null == node.getLeft()) {
            // deleted node only hae right child
            if (null  == node.getParent()) {
                this.root = node.getRight();
            } else if (node == node.getParent().getLeft()) {
                node.getParent().setLeft(node.getRight());
            } else {
                node.getParent().setRight(node.getRight());
            }
            node.getRight().setParent(node.getParent());
        } else if (null == node.getRight()) {
            // deleted node only have left child
            if (null  == node.getParent()) {
                this.root = node.getLeft();
            } else if (node == node.getParent().getLeft()) {
                node.getParent().setLeft(node.getLeft());
            } else {
                node.getParent().setRight(node.getLeft());
            }
            node.getLeft().setParent(node.getParent());
        } else {
            // deleted node have both left & right children
            // find out the precursor of deleted node
            // the precursor node replace the position of deleted node
            TreeNode pre = node.getLeft();
            while (null != pre.getRight()) {
                pre  = pre.getRight();
            }
            // swap the elem of precursor node and deleted node
            // then delete the precursor node
            TreeUtils.swapTreeElem(pre, node);
            deleteNode(pre);
        }
    }

寫個小程序測試一下:

輸入測試數據:10 5 2 7 6 18 13 -1(-1是結束輸入,不作為一個元素值)

1. insert
2. delete
3. search
4. print
5. exit
->1
->10 5 2 7 6 18 13 -1
insert success

看一下樹的結構,顯示樹結構的需要順時針轉90°來看,並自己想象節點連線。。

1. insert
2. delete
3. search
4. print
5. exit
->4
-
    18
        13
10
        7
            6
    5
        2
-

刪除節點 5 測試效果

1. insert
2. delete
3. search
4. print
5. exit
->2
->5
delete success
--------------------------------------------------------------
1. insert
2. delete
3. search
4. print
5. exit
->4
-
    18
        13
10
        7
            6
    2
-

至此, 二叉搜索樹的操作及具體實現完成,如有不妥之處,歡迎指出斧正。

尊重知識產權,轉載請標明出處並通知作者。

數據結構 - 從二叉搜索樹說到AVL樹(一)之二叉搜索樹的操作與詳解(Java)