二叉查找树

定义

二叉查找树(Binary Search Tree),也称二叉搜索树,是指一棵空树或者具有下列性质的二叉树:

  • 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 任意节点的左、右子树也分别为二叉查找树;
  • 没有键值相等的节点。

二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为O(log n)。二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、multiset、关联数组等。
这里写图片描述

二叉查找树是基于二叉树的,其结点数据结构定义为如下:

private class Node {
     private Key key;           
     private Value val;         // 存储的数据
     private Node left, right;  // 左或右子树
     private int size;          // 子树的节点数

     public Node(Key key, Value val, int size) {
          this.key = key;
          this.val = val;
          this.size = size;
      }
  }

查找操作

在二叉查找树中查找x的过程如下:

1、若二叉树是空树,则查找失败。

2、若x等于根结点的数据,则查找成功,否则。

3、若x小于根结点的数据,则递归查找其左子树,否则。

4、递归查找其右子树。

根据上述的步骤,写出其查找操作的代码:

    public boolean contains(Key key) {
        if (key == null) throw new IllegalArgumentException("argument to contains() is null");
        return get(key) != null;
    }

    public Value get(Key key) {
        return get(root, key);
    }

    private Value get(Node x, Key key) {
        if (key == null) throw new IllegalArgumentException("called get() with a null key");
        if (x == null) return null;
        int cmp = key.compareTo(x.key);
        if      (cmp < 0) return get(x.left, key);//递归进左子树查找
        else if (cmp > 0) return get(x.right, key);//递归进右子树查找
        else              return x.val;//查找命中
    }

插入操作

二叉树查找树b插入操作x的过程如下:

1、若b是空树,则直接将插入的结点作为根结点插入。

2、x等于b的根结点的数据的值,则直接返回,否则。

3、若x小于b的根结点的数据的值,则将x要插入的结点的位置改变为b的左子树,否则。

4、将x要出入的结点的位置改变为b的右子树。

这里写图片描述

代码如下:

public void put(Key key, Value val) {
        if (key == null) throw new IllegalArgumentException("calledput() with a null key");
        if (val == null) {
            delete(key);
            return;
        }
        root = put(root, key, val);
        assert check();
    }

    private Node put(Node x, Key key, Value val) {
        if (x == null) return new Node(key, val, 1);
        int cmp = key.compareTo(x.key);
        if      (cmp < 0) x.left  = put(x.left,  key, val);
        else if (cmp > 0) x.right = put(x.right, key, val);
        else              x.val   = val;
        x.size = 1 + size(x.left) + size(x.right);
        return x;
    }

删除操作

二叉查找树中最难实现的就是删除操作。下面是一个解决办法:
在删除节点x后,用他的后继节点填补它的位置。步奏如下:

1,将指向即将被删除的节点的链接保存为t;

2,将x指向它的后继节点min(t.right);

3,将x的右链接指向deleteMin(t.right);

4,将x的左链接(本为空)设为t.left。
这里写图片描述

代码如下:

//删除最小节点(热身)
public void deleteMin() {
    if (isEmpty()) throw new NoSuchElementException("Symbol table underflow");
    root = deleteMin(root);
}

private Node deleteMin(Node x) {
    if (x.left == null) return x.right;//不断检索左子树,直到遇到空的左链接,返回右链接
    x.left = deleteMin(x.left);
    return x;
}

//删除最大节点(热身)
public void deleteMax() {
    if (isEmpty()) throw new NoSuchElementException("Symbol table underflow");
    root = deleteMax(root);
}

private Node deleteMax(Node x) {
    if (x.right == null) return x.left;//不断检索右子树,直到遇到空的右链接,返回左链接
    x.right = deleteMax(x.right);
    return x;
}

//删除任意节点
 public void delete(Key key) {
        if (key == null) throw new IllegalArgumentException("called delete() with a null key");
        root = delete(root, key);
 }

 private Node delete(Node x, Key key) {
     if (x == null) return null;
     int cmp = key.compareTo(x.key);
     if      (cmp < 0) x.left  = delete(x.left,  key);
     else if (cmp > 0) x.right = delete(x.right, key);
     else { 
         if (x.right == null) return x.left;
         if (x.left  == null) return x.right;
         Node t = x;
         x = min(t.right);//将x指向它的后继节点
         x.right = deleteMin(t.right);//将x的右链接指向删除后所有节点都大于x.key的子二叉查找树
         x.left = t.left;
     } 
     return x;
 } 
  public Key min() {
        if (isEmpty()) throw new NoSuchElementException("called min() with empty symbol table");
        return min(root).key;
    } 

    private Node min(Node x) { 
        if (x.left == null) return x; 
        else                return min(x.left); 
    } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值