二叉排序 / 搜索树

基本介绍

1.二叉排序/搜索树:BST(Binary Sort/Search Tree),对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大

2.如果有相同的值,可以将该节点放在左子节点或右子节点,或不允许键值相等的节点

3.可高效的完成对数据的查询、添加、删除

4.缺陷:所有数据都在一侧子树,类似单链表,因为要比较另一侧子树,查询速度比单链表更慢

5.前驱节点:值小于当前节点的最大值节点

6.后继节点:值大于当前节点的最小值节点

7.在一棵二叉查找树中,所有操作在最坏情况下所需的时间都和树的高度成正比,树的所有操作都沿着树的一条或两条路径行进。根据定义,路径的长度不可能大于树的高度

 

二叉排序树删除节点思路分析

 

删除根节点

1.若找到要删除的结点target为根节点

2.判断根节点左右子节点是否为空

3.左右子节点不为空:找root右子树最小值的结点/左子树最大值的节点,用一个临时变量temp,将最小结点的值/最大节点的值保存,删除该最小结点/最大节点,用temp覆盖root的值

4.只有左节点:root = root.left

5.只有右节点:root = root.right

 

删除叶子节点

1.找到要删除的结点target

2.找到target的父结点parent

3.确定target是parent的左子结点还是右子结点

4.根据前面的情况来对应删除:左子结点parent.left = null;右子结点parent.right = null

 

删除非根、非叶子节点

有两颗子树:

1.找到要删除的结点target

2.找到target的父结点parent

3.找target右子树最小值的结点/左子树最大值的节点

4.用一个临时变量temp,将最小结点的值/最大节点的值保存

5.删除该最小结点/最大节点,用temp覆盖target的值

6.target.value = temp

只有一颗子树:

1.找到要删除的结点target

2.找到target的父结点parent

3.确定target的子结点是左子结点还是右子结点

4.target是parent的左子结点还是右子结点

(1)如果target有左子结点;如果target是parent的左子结点:parent.left = target.left;如果target是parent的右子结点:parent.right = target.left

(2)如果target有右子结点;如果target是parent的左子结点:parent.left = target.right;如果target是parent 的右子结点:parent.right = target.right

 

二叉排序树的添加规则影响删除节点

1.若添加相同值的节点,放入相同值节点的右子树

(1)删除根节点:左右子节点不为空,找root右子树最小值的节点,用一个临时变量temp,将最小节点的值保存,删除该最小节点,用temp覆盖root的值

(2)删除非根、非叶子节点:有两颗子树,找target右子树最小值的节点,用一个临时变量temp,将最小节点的值保存,删除该最小节点,用temp覆盖target的值

2.若添加相同值的节点,放入相同值节点的左子树

(1)删除根节点:左右子节点不为空,找root左子树最大值的节点,用一个临时变量temp,将最大节点的值保存,删除该最大节点,用temp覆盖root的值

(2)删除非根、非叶子节点:有两颗子树,找target左子树最大值的节点,用一个临时变量temp,将最大节点的值保存,删除该最大节点,用temp覆盖target的值

3.添加有相同值的节点,添加的节点放在相同值节点的尾部;删除有相同值的节点,删除找到的第一个节点

 

代码实现

public class BinarySortTree {
    public Node root;

    //添加节点,若有相同值的节点,则插入相同值节点的尾部
    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 delete(int value) {
        if (root == null) {
            return;
        }
        Node target = search(value);//找待删除的结点target
        if (target == null) {//没有找到
            return;
        }
        Node parent = searchParent(value);//找到target的父结点parent
        if (parent == null) {//说明待删除节点是root
            deleteRoot();
        } else if (target.left == null && target.right == null) {//说明待删除节点是叶子节点
            deleteLeaf(parent, value);
        } else {//说明删除非根、非叶子节点
            deleteNonLeaf(target, parent, value);
        }
    }

    //删除根节点
    public void deleteRoot() {
        if (root.left != null && root.right != null) {
            //删除root的后继节点,并用其值覆盖root的值
            root.value = deleteRightTreeMin(root);
            //删除root的前驱节点,并用其值覆盖root的值
            //root.value = deleteLeftTreeMax(root);
        } else if (root.left != null) {
            root = root.left;
        } else if (root.right != null) {//
            root = root.right;
        } else {//root.left == null && root.right == null
            root = null;
        }
    }

    //删除叶子节点
    public void deleteLeaf(Node parent, int value) {
        //确定target是parent的左子节点还是右子节点
        if (parent.left != null && parent.left.value == value) {
            parent.left = null;
        } else if (parent.right != null && parent.right.value == value) {
            parent.right = null;
        }
    }

    //删除非根、非叶子节点
    public void deleteNonLeaf(Node target, Node parent, int value) {
        //要删除的节点有两个子节点
        if (target.left != null && target.right != null) {
            //删除target的后继节点,并用其值覆盖target的值
            target.value = deleteRightTreeMin(target);
            //或删除target的前驱节点,并用其值覆盖target的值
            //target.value = deleteLeftTreeMax(target);
        } else if (target.left != null) {//要删除的节点只有一个左子节点
            if (parent.left.value == value) {//要删除的节点是父节点的左子节点
                parent.left = target.left;
            } else {//parent.right.value == value,要删除的节点是父节点的右子节点
                parent.right = target.left;
            }
        } else {//target.right != null,要删除的节点只有一个右子节点
            if (parent.left.value == value) {//要删除的节点是父节点的左子节点
                parent.left = target.right;
            } else {//parent.right.value == value,要删除的节点是父节点的右子节点
                parent.right = target.right;
            }
        }
    }

    //返回parent后继节点的值,并删除该后继节点
    public int deleteRightTreeMin(Node parent) {
        Node target = parent.right;
        if (target.left != null) {//parent的右子节点有左子节点
            parent = parent.right;
            target = target.left;
            while (target.left != null) {//循环的查找左节点,直到找到最小值
                parent = parent.left;
                target = target.left;
            }
            parent.left = target.right;//删除该最小值节点,此时target没有左右子节点或只有右节点
        } else {//target.left == null,parent的右子节点没有有左子节点
            parent.right = target.right;//删除该最小值节点,此时target没有左右子节点或只有右节点
        }
        return target.value;
    }

    //返回parent前驱节点的值,并删除该前驱节点
    public int deleteLeftTreeMax(Node parent) {
        Node target = parent.left;
        if (target.right != null) {//parent的左子节点有右子节点
            parent = parent.left;
            target = target.right;
            while (target.right != null) {//循环的查找右节点 直到找到最大值
                parent = parent.right;
                target = target.right;
            }
            parent.right = target.left;//删除该最大值节点,此时target没有左右子节点或只有左节点
        }else{//target.right == null,parent的左子节点没有有右子节点
            parent.left = target.left;//删除该最大值节点,此时target没有左右子节点或只有左节点
        }
        return target.value;
    }
}

class Node {
    public int value;
    public Node left;
    public Node right;

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

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

    //添加节点
    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 (node.value > this.value){
            if (this.right == null) {
                this.right = node;
            } else {
                this.right.add(node);
            }
        } else {//若是相同节点,则不变
            return;
        }
    }

    //查找节点,value:节点的值;找到则返回该节点,否则返回null
    public Node search(int value) {
        if (value == this.value) {//找到该节点
            return this;
        } else if (value < this.value) {
            if (this.left == null) {
                return null;
            } else {
                return this.left.search(value);//向左子树递归查找
            }
        } else {//value >= this.value
            if (this.right == null) {
                return null;
            } else {
                return this.right.search(value);//向右子树递归查找
            }
        }
    }

    //查找节点的父节点,value:节点的值;找到返回节点的父节点,否则返回null
    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;//没有父节点
            }
        }
    }
}

 

在由 N 个随机键构造的二叉查找树中

1、查找命中平均所需的比较次数为 2 * lnN(约 1.39 * lgN)

2、插入操作和查找未命中平均所需的比较次数为 2 * lnN(约 1.39 * lgN)

posted @   半条咸鱼  阅读(43)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示