二叉查找树

二叉查找树

又叫二叉搜索树,要求在树中的任何一个节点,其左子树中每个节点的值都要小于这个节点的值,右子树节点的值都要大于这个节点的值。

1、查找

思路:

先取根节点,若等于要查找的数据,返回
若小于要查找的数据,递归查找右子树
若大于要查找的数据,递归查找左子树

在这里插入图片描述

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

    public Node(int data, Node left, Node right){
        this.data = data;
        this.left = left;
        this.right = right;
    }

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                ", left=" + left +
                ", right=" + right +
                '}';
    }
}

public class BinarySearchTree {
    //根节点
    private Node root;

    public BinarySearchTree(Node root){
        this.root = root;
    }

    public Node find(int data){
        Node p = root;
        while (p != null){
            if (p.data > data) p = p.left;
            if (p.data < data) p = p.right;
            else return p;
        }
        return null;
    }

    public static void main(String[] args) {
        Node node13 = new Node(27, null, null);
        Node node12 = new Node(19, null, null);
        Node node11 = new Node(66, null, null);
        Node node10 = new Node(51, null, null);
        Node node9 = new Node(25, node12, node13);
        Node node8 = new Node(16, null, null);
        Node node7 = new Node(58, node10, node11);
        Node node6 = new Node(34, null, null);
        Node node5 = new Node(18, null, node9);
        Node node4 = new Node(13, null, node8);
        Node node3 = new Node(50, node6, node7);
        Node node2 = new Node(17, node4, node5);
        Node node1 = new Node(33, node2, node3);

        BinarySearchTree tree = new BinarySearchTree(node1);
        Node node = tree.find(19);
        System.out.println(node.toString());
    }
}

结果输出:

Node{data=19, left=null, right=null}

2、插入

思路:

从根节点开始,若要插入的数据比节点数据大,并且节点右子树为空,则将新数据插入到右子节点位置。

若右子树不为空,递归遍历右子树,查找插入位置。

若要插入的数据比节点数据小,并且节点左子树为空,则将新数据插入到左子节点位置。

若左子树不为空,递归遍历左子树,查找插入位置。

在这里插入图片描述

插入之前树结构:
Node{data=33, left=Node{data=17, left=Node{data=13, 
left=null, right=Node{data=16, left=null, right=null}}, 
right=Node{data=18, left=null, right=Node{data=25, 
left=Node{data=19, left=null, right=null}, 
right=Node{data=27, left=null, right=null}}}}, 
right=Node{data=50, left=Node{data=34, left=null, 
right=null}, right=Node{data=58, left=Node{data=51, 
left=null, right=null}, right=Node{data=66, left=null, 
right=null}}}}
public void insert(int data){
        Node p = root;
        while (p != null){
            if (p.data > data){
                if (p.left == null){
                    p.left = new Node(data,null,null);
                    return;
                }
                p = p.left;
            }else{
                if (p.right == null){
                    p.right = new Node(data,null,null);
                    return;
                }
                p = p.right;
            }
        }
    }
插入之后输出:
Node{data=33, left=Node{data=17, left=Node{data=13, 
left=null, right=Node{data=16, left=null, right=null}}, right=Node{data=18, left=null, right=Node{data=25, 
left=Node{data=19, left=null, right=null}, 
right=Node{data=27, left=null, right=null}}}}, 
right=Node{data=50, left=Node{data=34, left=null, 
right=null}, right=Node{data=58, left=Node{data=51, 
left=null, right=Node{data=55, left=null, right=null}}, 
right=Node{data=66, left=null, right=null}}}} 

3、删除

删除操作相对复杂一点,要分三种情况考虑

要删除的节点没有子节点:直接将父节点指向删除节点的指针置成null即可。

要删除的节点有一个子节点:将父节点指向删除节点的指针置成删除节点的子节点即可。

要删除的节点有两个子节点:找到删除节点右子树的最小节点,最小节点和删除节点值互换,然后删除最小节点即可。最小节点一定没有左子节点。

在这里插入图片描述

public void delete(int data){
        Node p = root;//指向要删除的节点,初始化为根节点
        Node pp = null;//指向要删除节点的父节点。初始化为null
        //查找要删除的节点
        while (p != null && p.data != data){
            pp = p;
            if (p.data < data) p = p.left;
            else p = p.right;
        }
        if (p == null) return;
        //要删除节点有两个节点
        if (p.left != null && p.right != null){
            Node minp = p.right;
            Node minpp = p;
            while (minp.left != null){
                minpp = minp;
                minp = minp.left;
            }
            //替换要删除的数据
            p.data = minp.data;
            //上面替换完,要删除的数据和最小数据一样了,
            //剩下的就是删除最小值就可以了
            //因为p指向的是要删除的,所以将p指向最小值
            p = minp;
            pp = minpp;
        }
        //删除没有子节点或者有一个节点
        //上面有两个子节点的问题已经转换成了没有子节点的问题
        Node child;//要删除节点P的子节点
        if (p.left != null) child = p.left;
        else if (p.right != null) child = p.right;
        else child = null;

        if (pp == null) root = child;//删除根节点
        else if (pp.left == p) pp.left = child;
        else pp.right = child;
    }

4、支持重复数据的二叉查找树

二叉查找树中节点的数据相同怎么办呢?有两种方法:

节点不仅存储数据,要存储一个链表或者数组,相同的数据放到链表或者数组中,类似于散列表。

相同的数据,放到此节点的右子节点中。这种情况在查找、删除的时候就不能找到一个就返回,要一直遍历到最后。

5、时间复杂度分析

在极度恶心的情况下,二叉树左右子树极度不平衡,变成了链表,此时的时间复杂度为O(n)。
在理想情况下,时间复杂度和树的高度成正比,也就变成了二叉树的高度问题。
根据完全二叉树的结构我们可以发现,第一层是1个节点,第二层是2个节点,第三层是4个节点,以此类推第K层就是2(k-1)个节点,而最后一层不一定是这样的,它的节点数在1~2(k-1)之间,那么总的节点数n的值范围就是:

n >= 1+2+4+8+...+2^(K-2)+1
n <= 1+2+4+8+...+2^(K-2)+2^(K-1)

层次K的范围是[log2(n+1), log2n +1],也就是说完全二叉树的层数小于等于log2n +1,树的高度是log2n。最后得出完全二叉树的时间复杂度为O(logn)

6、二叉查找树和散列表

散列表是无序的,若需要输出有序的数据,需要先排序,而二叉树只需要做一次中序遍历即可。

散列表扩容耗时很多

散列表存在哈希冲突,当哈希冲突到达一定程度时,时间复杂度虽然是常量的,但是是比O(logn)大的。

散列表的构造过程要比二叉树复杂,需要考虑散列函数、冲突、扩容、缩容等问题

7、扩展问题

如何求出一颗二叉树的确切高度?

递归法,根节点高度 = max(左子树高度,右子树高度)+1

原文:https://gper.club/articles/7e7e7f7ff7g54gccg68

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值