二分搜索树学习笔记

二叉搜索树的定义

    若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉搜索树。

用JAVA定义二叉搜索树的节点

TreeNode有三个成员变量,int类型的变量val用来存储节点对应的值,TreeNode类型的节点left指向一颗由小于该节点的所有节点组冲的二叉搜索树,right指向一颗由大于该节点的所有节点组冲的二叉搜索树。
public class TreeNode {
	int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x; }
 }

二叉搜索树的相关操作

1.查找
一般来说,在符号表中查找一个键可能得到两种结果。如果含有该键的结点存在于表中,我们的查找就命中了,然后返回相应的值。否则查找未命中(并返回null)。根据数据表示的递归结构我们马上就能得到,在二又查找树中查找一个键的递归算法:如果树是空的,则查找未命中;如果被查找的键和根结点的键相等,查找命中,否则我们就(递归地)在适当的子树中继续查找。如果被查找的键较小就选择左子树,较大则选择右子树。查找使用的函数get()如下:

private TreeNode get(TreeNode root,int v){
    if(root==null){
        return null;
    }
    if(root.getVal()==v) {
        return root;
    }
    else if(root.getVal()>v){
        return get(root.left,v);
    }
    else {
        return get(root.right,v);
    }

}
    2.插入
    插入函数的实现逻辑和递归查找很相似:如果树是空的,就返回一个含有该键值对的新结点;如果被查找的键小于根结点的键,我们会继续在左子树中插人该键,否则在右子树中插人该键。插入使用的put函数如下
private TreeNode put(TreeNode node, int v){

    if(node==null){
        TreeNode newNode = new TreeNode(v);
        return newNode;
    }
    if(v<node.val){
        node.left = put(node.left,v);
    }
    else{
        node.right = put(node.right,v);
    }
    
    return node;
}

3.获取二叉查找树中最大/最小值节点

   如果根结点的左链接为空,那么一棵二叉查找树中最小的键就是根结点;如果左链接非空,那么树中的最小键就是左子树中的最小键。这描述了min()方法的递归实现,找出最大键的方法也是类似的,只是变为查找右子树而已。
   min()方法的JAVA代码如下:
private TreeNode min(TreeNode root){
    if(root==null){
        return null;
    }
    if(root.left==null){
        return root;
    }
    return min(root.left);
}

max()方法的JAVA代码如下:

private TreeNode max(TreeNode root){
    if(root==null){
        return null;
    }
    if(root.right==null){
        return root;
    }
    return min(root.right);
}

4.向上取整和向下取整
向下取整(floor):如果给定的键key小于二叉查找树的根结点的键,那么小于等于key的最大键(floor)一定在根结点的左子树中;如果给定的键key大于二叉查找树的根结点,那么只有当根结点右子树中存在小于等于key的结点时,小于等于key的最大键才会出现在右子树中,否则根结点就是小于等于key的最大键。这段描述说明了floor()方法的递归实现。
将“左”变为“右”(同时将小于变为大于)就能够得到ceil()的算法。
向下取整函数的JAVA代码如下:

private TreeNode floor(TreeNode root,int v){
    if(root==null)
        return null;
    if(root.getVal()==v)
        return root;
    else if(root.getVal()>v)
        return floor(root.left,v);
    /*用t来记录从root的右子树中是否找到了v的floor值*/
    TreeNode t = floor(root.right, v);
    /*如果右子树中没有floor值那么root就是floor值*/
    if(t==null)
        return root;
    else
        return t;
}

向上取整函数的JAVA代码如下:

private TreeNode ceil(TreeNode root,int v){
    if(root==null)
        return null;
    if(root.getVal()==v)
        return root;
    else if(root.getVal()<v)
        return ceil(root.right,v);
    /*用t来记录从root的左子树中是否找到了v的ceil值*/
    TreeNode t = ceil(root.left, v);
    /*如果左子树中没有ceil值那么root就是ceil值*/
    if(t==null)
        return root;
    else
        return t;

}

5.选择操作
假设我们想找到排名为k的键。如果左子树中的结点数t加上父节1即t+1大于k,那么我们就继续(递归地)在左子树中查找排名为k的键;如果等于k,我们就返回根结点中的键;
如果小于k我们就(递归地)在右子树中查找排名为(k-t)的键。这段描述既说明了select()方法的递归实现。
选择操作的JAVA代码如下,其中size()函数用来计算树的节点数目

 public int select(TreeNode root, int k) {
        int size = size(root.left,k)+1;
        if(size==k)
            return root.val;
        else if(size>k)
            return select(root.left,k);
        else
            return select(root.right,k-size);
        
    }
    
//     定义size()函数用来求以root为根节点的树有多少个
    private int size(TreeNode root,int k){
        if(root==null)
            return 0;
        int left_size = size(root.left,k);
        int right_size = size(root.right,k);
        return left_size+right_size+1;
    }

6.排名
rank()是select()的逆方法,它会返回给定键的排名。它的实现和select()类似:如果给定的键和根结点的键相等,我们返回左子树中的结点总数加1;如果给定的键小于根结点,我们会返回该键在左子树中的排名(递归计算)如果给定的键大于根结点,我们会返回根结点的排名加上它在右子树中的排名(递归计算)。

private int rank(TreeNode root,int v){
   if(root==null){
       return 1;
   }
   if(v<root.val()){
       return rank(root.left,v);
   }
   else if(v>root.val()){
       return rank(root.right,v)+size(root.left)+1;
   }
   else {
       return size(root.left)+1;
   }
}

7.排序
二叉搜索树的中序遍历结果就是二叉搜索树按照升序排序的结果。

 private void inOrder(TreeNode root, List<Integer> ret){
        if(root==null)
            return ;
        inOrder(root.left);
        ret.add(root.val);
        inOrder(root.right);
        
    }

8.删除二叉搜索树的任一节点
删除二叉搜索树任意节点的步骤如下:
(1)将指向即将被删除的结点的链接保存为t
(2)将x指向它的后继结点min(t.right);
(3)将x的右链接(原本指向一棵所有结点都大于x.val的二叉查找树)指向deleteMin(t.right),也就是在删除后所有结点仍然都大于x.val的子二叉查找树;
(4)将x的左链接(本为空)设为t.left(其下所有的键都小于被删除的结点和它的后继结点)。
删除操作的JAVA代码为:

public TreeNode deleteNode(TreeNode root, int key) {
        if(root==null){
            return null;
        }
        if(root.val==key) {
            TreeNode x = getMin(root.right);
            if(x==null){
                return root.left;
            }
            TreeNode rightNode = deleteMin(root.right);
            x.right = rightNode;
            x.left = root.left;
            root = null;
            return x;
        }
        else if(root.val>key){
            root.left = deleteNode(root.left,key);
        }
        else {
            root.right = deleteNode(root.right,key);
        }
            return root;
    }
    
    private TreeNode deleteMin(TreeNode root){
        if(root==null){
            return null;
        }
        if(root.left==null){
            return root.right;
        }
        TreeNode rightNode = deleteMin(root.left);
        root.left = rightNode;
        return root;
    }
    
     private TreeNode getMin(TreeNode root){
        if(root==null){
            return null;
        }
        if(root.left==null){
            return root;
        }
        return getMin(root.left);
    }

参考文献:
Robert Sedgewick,Kevin Wayne 算法第4版

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值