二叉排序樹刪除節點詳解


二叉排序樹刪除節點詳解

說明

  1. 二叉排序樹有着比數組查找快,比鏈表增刪快的優勢,比較常見
  2. 二叉排序樹要刪除節點,首先要找到該節點和該節點對應的父節點,因為只根據當前節點是不能刪除節點本身的,因此需要找到父節點
  3. 二叉排序樹刪除節點,根據節點所在的位置不同,需要分為三種情況
  4. 即要刪除的節點是葉子節點,要刪除的節點只有一顆子樹的情況和要刪除的節點有兩顆子樹的情況
  5. 考慮第一種情況,即要刪除的節點是葉子節點
  6. 直接找到要刪除節點的父節點,然后置空即可
  7. 考慮第二種情況,即要刪除的節點有一顆子樹
  8. 先找到該節點和該節點的父節點,根據該節點是父節點的左子節點還是右子節點和該節點有左子節點還是有有子節點,應該分為四種情況討論,每種情況改變父節點的引用即可實現刪除
  9. 考慮第三種情況,即要刪除的節點有兩顆子樹
  10. 這種情況刪除思路應該為找到當前節點右子樹的的最大值節點或者找到當前節點左子樹的最小值節點,先將這個節點刪除,再用這個節點替換當前節點
  11. 實質操作先記錄刪除子樹的最大最小值節點的值,然后刪除這個節點,最后用記錄的這個值替換掉要刪除的節點,實現刪除有左右子樹節點的思路
  12. 源碼及思路見下

源碼及分析

package algorithm.tree.bst;

/**
 * @author AIMX_INFO
 * @version 1.0
 */
public class BinarySortTree {
    public static void main(String[] args) {
        int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
        BST bst = new BST();
        //循環添加節點
        for (int i = 0; i < arr.length; i++) {
            bst.add(new Node(arr[i]));
        }
        //中序遍歷查看
        System.out.println("中序遍歷");
        bst.infixOrder();
        System.out.println("刪除葉子節點");
        bst.delNode(7);
        bst.infixOrder();
    }
}

//二叉排序樹
class BST {
    private Node root;

    //中序遍歷
    public void infixOrder() {
        if (root != null) {
            root.infixOrder();
        } else {
            System.out.println("樹是空的");
        }
    }

    //添加節點
    public void add(Node node) {
        if (root == null) {
            root = node;
        } else {
            root.add(node);
        }
    }

    //查找某一節點
    public Node search(int value) {
        if (root == null) {
            return null;
        } else {
            return root.search(value);
        }
    }

    //查找某一節點的父節點
    public Node searchParent(int value) {
        if (root == null) {
            return null;
        } else {
            return root.searchParent(value);
        }
    }

    //刪除節點
    public void delNode(int value) {
        //先判斷是否為空樹
        if (root == null) {
            return;
        } else {
            //如果樹不為空,再判斷樹是否只有一個空節點
            if (root.left == null && root.right == null && root.value == value) {
                root = null;
                return;
            }
            //否則先查找要刪除的節點
            Node target = search(value);
            //判斷要刪除節點是否存在
            if (target == null) {
                return;
            }
            //如果存在則再找到要刪除節點的父節點
            Node parent = searchParent(value);
            //然后根據要刪除的節點分情況刪除
            //如果要刪除的節點是葉子節點
            if (target.left == null && target.right == null) {
                //判斷target是父節點的左子節點還是右子節點
                //如果是左子節點
                if (parent.left != null && parent.left.value == value) {
                    parent.left = null;
                }
                //如果是左子節點
                if (parent.right != null && parent.right.value == value) {
                    parent.right = null;
                }
                //如果要刪除的節點有兩個子節點
            } else if (target.left != null && target.right != null) {
                int minVal = delRightNodeMin(target.right);
                target.value = minVal;

                //否則要刪除的節點只有一個子節點
            } else {
                //判斷要刪除的節點有左子節點還是右子節點
                //target的左子節點不為空
                if (target.left != null){
                    //判斷target是父節點的左子樹還是右子樹
                    //左子樹
                    if (parent != null) {
                        if (parent.left.value == value) {
                            parent.left = target.left;
                        } else {
                            //右子樹
                            parent.right = target.left;
                        }
                    }else {
                        root = target.left;
                    }
                    //target的右子節點不為空
                }else {
                    //同理
                    if (parent != null) {
                        if (parent.left.value == value) {
                            parent.left = target.right;
                        } else {
                            parent.right = target.right;
                        }
                    }else {
                        root = target.right;
                    }
                }
            }
        }
    }

    /**
     * 查找當前二叉排序樹的最小節點並刪除
     * @param node  當前二叉排序樹
     * @return 返回最小節點的值
     */
    public int delRightNodeMin(Node node){
        //輔助變量用於遍歷二叉排序樹
        Node target = node;
        //循環查找最小節點
        while (target.left != null){
            target = target.left;
        }
        //循環結束時已經找到
        //刪除當前節點
        delNode(target.value);
        //返回當前節點的值
        return target.value;
    }

}

//節點
class Node {
    int value;
    Node left;
    Node right;

    public Node(int value) {
        this.value = value;
    }

    //查找某一節點

    /**
     * @param value 要查找的節點的值
     * @return 返回查找的結果
     */
    public Node search(int value) {
        //如果要查找的節點就是當前節點,直接返回
        if (value == this.value) {
            return this;
        }
        //判斷要查找的節點的value與當前節點的value的大小關系
        //向左遞歸查找
        if (value < this.value) {
            //判斷左子樹是否為空
            if (this.left == null) {
                return null;
            }
            return this.left.search(value);
        } else {
            //向右遞歸查找
            //判斷右子樹是否為空
            if (this.right == null) {
                return null;
            }
            return this.right.search(value);
        }
    }
    //查找要刪除節點的父節點

    /**
     * @param value 要刪除的節點的值
     * @return 返回查找的結果
     */
    public Node searchParent(int value) {
        //判斷當前節點是不是要查找節點的父節點
        if ((this.left != null && this.left.value == value) ||
                (this.right != null && this.right.value == value)) {
            return this;
        } else {
            //如果不是則向左向右遞歸查找
            //向左遞歸
            if (value < this.value && this.left != null) {
                return this.left.searchParent(value);
                //向右遞歸
            } else if (value >= this.value && this.right != null) {
                return this.right.searchParent(value);
            } else {
                //否則沒有找到
                return null;
            }
        }
    }

    //遞歸添加節點的方法
    public void add(Node node) {
        //數據校驗
        if (node == null) {
            return;
        }
        //根據要添加的節點的值和當前節點值的大小判斷節點要添加的位置
        if (node.value < this.value) {
            //如果當前左子節點為空,則直接添加
            if (this.left == null) {
                this.left = node;
            } else {
                //否則遞歸添加
                this.left.add(node);
            }
        } else {
            //同理
            if (this.right == null) {
                this.right = node;
            } else {
                this.right.add(node);
            }
        }
    }

    //中序遍歷
    public void infixOrder() {
        if (this.left != null) {
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right != null) {
            this.right.infixOrder();
        }
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM