数据结构:二分搜索树底层实现

二分搜索树的定义

#二分搜索树是二叉树
#二分搜索树的每个节点的值:
1.大于左子树的所有节点的值
2.小于右子树的所有节点的值

#存储的元素要具有可比较性。例如:如果存储学生对象,可以比较学号。

二分搜索树代码结构

public class BST<E extends Comparable<E>> { private Node root;
    private int size;

    private class Node {
        public E e;
        public Node left, right;

        public Node(E e) {
            this.e = e;
            this.left = null;
            this.right = null;
        }
    }

    public BST() {
        this.root = null;
        this.size = 0;
    }

    public int getSize() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

}

向二分搜索树添加元素(递归实现)

public void add(E e) {
    if (root == null) {
        root = new Node(e);
        size++;
    } else
        add(root, e);
}

private void add(Node node, E e) {
//递归终止条件
    if (e.equals(node.e))
        return;

    else if (e.compareTo(node.e) < 0 && node.left == null) {
        node.left = new Node(e);
        size++;
        return;
    } else if (e.compareTo(node.e) > 0 && node.right == null) {
        node.left = new Node(e);
        size++;
        return;
    }
//递归调用
    if (e.compareTo(node.e) < 0)
        add(node.left, e);
    else//e.compareTo(node.e) > 0

        add(node.right, e);
}

进一步简化代码:

public void add(E e) {
    root = add(root, e);
}

private Node add(Node node, E e) {

    if (node == null) {
        size++;
        return new Node(e);
    }

    if (e.compareTo(node.e) < 0)
        node.left = add(node.left, e);
    else if (e.compareTo(node.e) > 0)
        node.right = add(node.right, e);

    return node;
}

Contains方法(递归实现)

public boolean contains(E e) {
    return contains(root, e);
}

private boolean contains(Node node, E e) {
    if (node == null)
        return false;

    if (e.compareTo(node.e) == 0)
        return true;

    
    else if (e.compareTo(node.e) < 0)
        return contains(node.left, e);
    else //e.compareTo(node.e) > 0
        return contains(node.right, e);
}

前序遍历(递归实现)

public void preorderTraversal() {
    preorderTraversal(root);
}

private void preorderTraversal(Node node) {

    if (node == null)
        return;

    System.out.println(node.e);
    preorderTraversal(node.left);
    preorderTraversal(node.right);
}

中序遍历(递归实现)

public void intermediateTraversal(){
    intermediateTraversal(root);
}

private void intermediateTraversal(Node node) {
    if (node==null)
        return;

    intermediateTraversal(node.left);
    System.out.println(node.e);
    intermediateTraversal(node.right);
}

后序遍历(递归实现)

public void postorderTraversal(){
    postorderTraversal(root);
}

private void postorderTraversal(Node node) {

    if (node==null)
        return;

    postorderTraversal(node.left);
    postorderTraversal(node.right);
    System.out.println(node.e);
}

前序遍历(非递归实现)

使用栈实现:
说明:
1.先将根节点压入栈,出栈,记录,并将左右孩子节点压入(先压入右孩子节点,后压入左孩子节点)
2.出栈(即左孩子节点(栈顶元素)先出栈),记录,并查看左孩子节点是否有孩子节点,仍然是右孩子节点先压入栈,然后压入左孩子节点。如果没有孩子,则出栈现在栈顶元素(右孩子节点),记录。
3.循环以上过程

public void preorderTraversalNR() {
    Stack<Node> stack = new Stack<>();
    stack.push(root);
    while (!stack.isEmpty()) {
        Node cur = stack.pop();
        System.out.println(cur.e);

        if (cur.right != null)
            stack.push(cur.right);
        if (cur.left != null)
            stack.push(cur.left);
    }
}

层序遍历(也称广度优先遍历)(非递归实现)

使用队列实现:
说明:
1.先将根节点入队,出队,记录。并将根节点的左右孩子节点入队(左孩子节点先入队,右孩子节点后入队)
2.入队完成后,队首元素出队,记录,并将其左右孩子入队(仍然按照左先右后的顺序)
3.由于队列先进先出的性质,每次都是左孩子节点先出队,右孩子后出队。并且左孩子节点和右孩子节点都入队,左孩子节点出队后,压入左孩子节点的左右孩子节点之前,上一层右孩子节点为队首,所以下层的左右孩子节点在上层的右孩子节点之后,所以可以做到层序遍历。

public void levelTraversal(){
    Queue<Node> queue = new LinkedList<>();
    queue.add(root);
    while (!queue.isEmpty()){
        Node cur = queue.remove();
        System.out.println(cur.e);

        if (cur.left!=null)
            queue.add(cur.left);
        if (cur.right!=null)
            queue.add(cur.right);
    }
}

查找最值

由于二分搜索树的特点,我们可以得到
最小值:一直向左走,直到左子树为null
最大值:一直向右走,直到右子树为null
注意:最值不一定是叶子节点。
如下图所示:
情况一:在这里插入图片描述
情况二:在这里插入图片描述

查找最小值(递归实现)

public E minimumValue(){
if (size == 0)
    throw new IllegalArgumentException("BST is Empty!");

    return minimumValue(root);
}

private E minimumValue(Node node) {
    if (node.left==null)
        return node.e;
    return minimumValue(node.left);
}

查找最小值(非递归实现)

public E minimumValueNR() {
    if (size == 0)
        throw new IllegalArgumentException("BST is Empty!");

    Node cur = root;
    while (cur.left!=null)
        cur = cur.left;
    return cur.e;
}

查找最大值(递归实现)

public E maximumValue(){
if (size == 0)
    throw new IllegalArgumentException("BST is Empty!");

    return maximumValue(root);
}

private E maximumValue(Node node) {
    if (node.right==null)
        return node.e;
    return maximumValue(node.right);
}

查找最大值(递归非实现)

public E maximumValueNR() {
    if (size == 0)
        throw new IllegalArgumentException("BST is Empty!");

    Node cur = root;
    while (cur.right != null)
        cur = cur.right;
    return cur.e;
}

删除最小值

情况①:最小值为叶子节点(如下图)
操作:直接删除就好了
在这里插入图片描述
情况②:最小值为非叶子节点(如下图)
操作:将删除节点的右子树变成删除节点的父亲节点的左子树在这里插入图片描述
删除后:在这里插入图片描述

删除最小值(递归实现)

public E removeMin() {
    E ret = minimumValueNR();
    root = removeMin(root);
    return ret;
}

//删除以node为根的最小值
//返回删除节点后新的二分搜索树的跟
private Node removeMin(Node node) {

    if (node.left==null){
        Node rightNode = node.right;
        node.right=null;
        size--;
        return rightNode;
    }
    node.left = removeMin(node.left);
    return node;
}

删除最小值(非递归实现)

public E removeMinNR() {
    E ret = minimumValueNR();
    Node cur = root;
    if (cur.left == null) {
        Node rightNode = cur.right;
        cur.right = null;
        root = rightNode;
        size--;
        return ret;
    }
    while (cur.left.left != null) {
        cur = cur.left;
}
    if (cur.left.right != null) {
        Node rightNode = cur.left.right;
        cur.left = rightNode;
    } else
        cur.left = null;

    size--;
    return ret;
}

删除最大值

情况①:最大值为叶子节点(如下图)
操作:直接删除就好了
情况②:最大值为非叶子节点(如下图)
操作:将删除节点的左子树变成删除节点的父亲节点的右子树

删除最大值(递归实现)

public E removeMax() {
    E ret = maximumValueNR();
    root = removeMax(root);
    return ret;
}
//删除以node为根的最大值
//返回删除节点后新的二分搜索树的跟
private Node removeMax(Node node) {
    if (node.right == null) {
        Node leftNode = node.left;
        node.left = null;
        size--;
        return leftNode;
    }
    node.right = removeMax(node.right);
    return node;
}

删除最大值(非递归实现)

public E removeMaxNR() {

    E ret = maximumValueNR();
    Node cur = root;
    if (cur.right == null) {
        Node leftNode = cur.left;
        cur.left = null;
        root = leftNode;
        size--;
        return ret;
    }
    while (cur.right.right != null)
        cur = cur.right;

    if (cur.right.left != null) {
        Node leftNode = cur.right.left;
        cur.right = leftNode;
    } else
        cur.right = null;
    size--;
    return ret;
}

删除任意元素

情况①:待删除节点只有左孩子
操作:左孩子节点取而代之

情况②:待删除节点只有有孩子
操作:右孩子取而代之

情况③(⭐):待删除节点左右都有孩子
如图:
在这里插入图片描述
操作:找待删除右子树最小元素取而代之 (或者左子树最大元素)
在这里插入图片描述

public void remove(E e) {
    root = remove(root, e);
}

private Node remove(Node node, E e) {

    if (node == null)
        return null;

    if (e.compareTo(node.e) < 0) {
        node.left = remove(node.left, e);
        return node;
    } else if (e.compareTo(node.e) > 0) {
        node.right = remove(node.right, e);
        return node;
    } else {//e.compareTo(node.e) == 0
        if (node.left == null) {
            Node rightNode = node.right;
            node.right = null;
            size--;
            return rightNode;
        }
        if (node.right == null) {
            Node leftNode = node.left;
            node.left = null;
            size--;
            return leftNode;
        }

        Node successor = minimumValue(node.right);
        successor.right = removeMin(node.right);
        successor.left = node.left;

        node.left = node.right = null;
        return successor;
    }

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值