查找算法总结之二(动态查找表)

刚刚介绍完静态查找树查找算法总结(一),接下来谈谈一些常用的动态查找结构。其中包括最基本的二叉排序树(二叉查找树、二叉收索树)、二叉平衡树(AVL树)、红黑树、以及一些多路查找树(B+,B-树)

二叉排序树

特点:
1、如果它的左子树不空,那么左子树上的所有结点值均小于它的根结点值;
2、如果它的右子树不空,那么右子树上的所有结点值均大于它的根结点值;
3、它的左右子树也分别为二叉查找树
如下如所示二叉查找树:
二叉查找树

二叉查找树的插入和删除都非常的方便,很好的解决了折半查找添加删除所带来的问题。

那么它的效率又如何呢?

很显然,二叉查找树查找一个数据,不需要遍历全部的节点,查找效率确实提高了。但是,也有一个很严重的问题,我在a图中查找8需要比较5次,而在b图中查找8需要3次,更为严重的是,我的二叉查找树是c图,如果再查找8,那将会如何呢?很显然,整棵树就退化成了一个线性结构,此时再查找8,就和顺序查找没什么区别了。

时间复杂度分析:最坏的情况下和顺序查找相同,是O(N),最好的情况下和折半查找相同,是O(logN)。进过研究发现,在随机的情况下,二叉排序树的平均查找长度和logn是等数量级的。然而,在某些情况下(有人研究表明,这种概率大约占46.5%)尚需在构成二叉排序树的过程中进行”平衡化”处理,成为二叉平衡树。

这说明了一个问题,同样的一组数据集合,不同的添加顺序会导致二叉查找树的结构完全不一样,直接影响到了查找的效率。

二叉排序树的一些基本操作,包括,添加节点,删除节点,获取做大的节点,获取最小的节点,得到任意孩子节点的父节点等。

/**
     * 向二叉排序树中添加节点
     * @param root
     * @param value
     * @return 二叉排数树根节点
     */
    public static TreeNode insertTreeNode(TreeNode root,int value){
        if(root == null){
            root = new TreeNode(value);
        }else if(root.value>value){
            root.left = insertTreeNode(root.left,value);
        }else if(root.value<value){
            root.right =insertTreeNode(root.right,value);
        }
        return root;
    }
/**
     * 在二叉排数树中查找,关键字为key的节点
     * @param root
     * @param key
     * @return 二叉排数树中关键字为key的节点
     */
    public static TreeNode serachTreeNode(TreeNode root,int key){
        if(root == null){
            return null;
        }
        if(key>root.value)
            return serachTreeNode(root.right,key);  
        else if(key<root.value)
            return serachTreeNode(root.left,key);  
        else  
            return root;  
    }
//获取父节点
public static TreeNode getParentTreeNode (TreeNode root,int key){
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        if(root==null || root.value==key){
            return null;
        }
        queue.add(root);
        while(!queue.isEmpty()){
            TreeNode current = queue.poll();
            if(current.left!=null){
                if(current.left.value==key)
                    return current;
                else
                    queue.add(current.left);
            }
            if(current.right!=null){
                if(current.right.value==key)
                    return current;
                else
                    queue.add(current.right);
            }
        }
        return null;
//获得关键字最大的节点
    public static TreeNode getMaxTreeNode(TreeNode root){
        if(root == null || root.right==null)
            return root;
        if(root.right != null){
            root = getMaxTreeNode(root.right);
        }
        return root;
    }
    //获得关键字最小的节点
    public static TreeNode getMinTreeNode(TreeNode root){
        if(root == null || root.left==null)
            return root;
        if(root.left != null){
            root = getMinTreeNode(root.left);
        }
        return root;
    }
    //打印排序二叉树
    public static void printInOrder(TreeNode root){
        if(root!=null){
            printInOrder(root.left);
            System.out.print(root.value+" ");
            printInOrder(root.right);           
        }
    }

最后关于二叉排序树的删除相对比较复杂,可以分下面三种情况讨论:

(1)需要删除的节点下并没有其他子节点,其为叶子节点。

(2)需要删除的节点下有一个子节点(左或右)。

(3)需要删除的节点下有两个子节点(既左右节点都存在)。

二叉排序树的删除

  • 针对第一种情况:删除叶子节点,例如删除4,只需判断4是它的父节点的左孩子,还是右孩子。如图中,4则为其父节点3的右孩子,所以直接将3的右孩子置为空。

  • 针对第二种情况:要删除的节点有一个孩子节点,若其为其父节点的右孩子,则让其父节点的右指针指向其孩子节点;若其为其父节点的左孩子,则让其父节点的左指针指向其孩子节点。

  • 针对第三种情况:要删除的节点既有左孩子又有右孩子,例如节点2,则我们先在需要删除的节点的右子树中,找到一个最小的值(因为右子树中的节点的值一定大于根节点)4。然后,用找到的最小的值与需要删除的节点的值替换。然后,再将最小值的原节点进行删除.

public static void deleteTreeNode(TreeNode root,TreeNode pNode){
        TreeNode parent = getParentTreeNode(root,pNode.value);
        //该节点是叶子节点
        if(pNode.left==null && pNode.right==null){
            //该节点是父节点的左孩子
            if(parent.left==pNode)
                parent.left=null;
            else
                parent.right=null;
        //该节点只有右孩子
        }else if(pNode.left==null&&pNode.right!=null){
            //该节点为其父节点的左孩子
            if(pNode == parent.left ){
                parent.left = pNode.right;
            }else{
                parent.right = pNode.right;
            }
        //该节点只有右孩子
        }else if(pNode.left!=null&&pNode.right==null){
            if(pNode ==parent.left ){
                parent.left = pNode.left;
            }else{
                parent.right = pNode.left;
            }
        //该节点左右孩子都存在
        }else if(pNode.left!=null&&pNode.right!=null){
            /*
             * 我们先在需要删除的节点的右子树中,找到一个最小的值(因为右子树中的节点的值一定大于根节点)。
             * 然后,用找到的最小的值与需要删除的节点的值替换。然后,再将最小值的原节点进行删除
             * */           
            TreeNode minNode=getMinTreeNode(pNode.right);
            deleteTreeNode(root,minNode);
            TreeNode pNpdeparent = getParentTreeNode(root,pNode.value);
            if(pNpdeparent.left==pNode){
                pNpdeparent.left=minNode;
                minNode.left = pNode.left;
                minNode.right = pNode.right;
            }               
            else{
                pNpdeparent.right=minNode;
                minNode.left = pNode.left;
                minNode.right = pNode.right;
            }               
        }
    }   

最后附上有关二叉排序树的基本操作的代码:(可直接运行)

输入:两行,第一行为二叉树节点的个数,第二行为各个节点的值(不重复)
10
89 67 34 30 40 50 44 55 66 77
输出:逐步按输入删除节点(除根节点)后二叉排序树的中序遍历(有序)
30 34 40 44 50 55 66 67 77 89
30 34 40 44 50 55 66 77 89
30 40 44 50 55 66 77 89
40 44 50 55 66 77 89
44 50 55 66 77 89
44 55 66 77 89
55 66 77 89
66 77 89
77 89
89

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.LinkedList;
import java.util.Queue;

class TreeNode {
    public int value;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(int value){
        this.value = value;
        this.left = this.right = null;
    }
}
public class BinarySearchTree {
    /**
     * 向二叉排序树中添加节点
     * @param root
     * @param value
     * @return 二叉排数树根节点
     */
    public static TreeNode insertTreeNode(TreeNode root,int value){
        if(root == null){
            root = new TreeNode(value);
        }else if(root.value>value){
            root.left = insertTreeNode(root.left,value);
        }else if(root.value<value){
            root.right =insertTreeNode(root.right,value);
        }
        return root;
    }
    /**
     * 在二叉排数树中查找,关键字为key的节点
     * @param root
     * @param key
     * @return 二叉排数树中关键字为key的节点
     */
    public static TreeNode serachTreeNode(TreeNode root,int key){
        if(root == null){
            return null;
        }
        if(key>root.value)
            return serachTreeNode(root.right,key);  
        else if(key<root.value)
            return serachTreeNode(root.left,key);  
        else  
            return root;  
    }
    public static void deleteTreeNode(TreeNode root,TreeNode pNode){
        TreeNode parent = getParentTreeNode(root,pNode.value);
        //该节点是叶子节点
        if(pNode.left==null && pNode.right==null){
            //该节点是父节点的左孩子
            if(parent.left==pNode)
                parent.left=null;
            else
                parent.right=null;
        //该节点只有右孩子
        }else if(pNode.left==null&&pNode.right!=null){
            //该节点为其父节点的左孩子
            if(pNode == parent.left ){
                parent.left = pNode.right;
            }else{
                parent.right = pNode.right;
            }
        //该节点只有右孩子
        }else if(pNode.left!=null&&pNode.right==null){
            if(pNode ==parent.left ){
                parent.left = pNode.left;
            }else{
                parent.right = pNode.left;
            }
        //该节点左右孩子都存在
        }else if(pNode.left!=null&&pNode.right!=null){
            /*
             * 我们先在需要删除的节点的右子树中,找到一个最小的值(因为右子树中的节点的值一定大于根节点)。
             * 然后,用找到的最小的值与需要删除的节点的值替换。然后,再将最小值的原节点进行删除
             * */           
            TreeNode minNode=getMinTreeNode(pNode.right);
            deleteTreeNode(root,minNode);
            TreeNode pNpdeparent = getParentTreeNode(root,pNode.value);
            if(pNpdeparent.left==pNode){
                pNpdeparent.left=minNode;
                minNode.left = pNode.left;
                minNode.right = pNode.right;
            }               
            else{
                pNpdeparent.right=minNode;
                minNode.left = pNode.left;
                minNode.right = pNode.right;
            }               
        }
    }   
    //获取父节点
    /*
      public static TreeNode getParentTreeNode(TreeNode root,int key){
            if(root!=null){
                if(root.left!=null && root.right!=null){
                    if(root.left.value == key || root.right.value ==key){
                        return root;
                    }else{
                        TreeNode tempRoot= getParentTreeNode(root.left,key);
                        if(tempRoot!=null)
                            return tempRoot;
                        root = getParentTreeNode(root.right,key);
                    }
                }else if(root.left==null && root.right!=null){
                    if(root.right.value ==key){
                        return root;
                    }else{
                        root = getParentTreeNode(root.right,key);
                    }
                }else if(root.left!=null && root.right==null){
                    if(root.left.value ==key){
                        return root;
                    }else{
                        root = getParentTreeNode(root.left,key);
                    }
                }
                else {
                    return null;
                }
            }
            return root;
        }
        */
    public static TreeNode getParentTreeNode (TreeNode root,int key){
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        if(root==null || root.value==key){
            return null;
        }
        queue.add(root);
        while(!queue.isEmpty()){
            TreeNode current = queue.poll();
            if(current.left!=null){
                if(current.left.value==key)
                    return current;
                else
                    queue.add(current.left);
            }
            if(current.right!=null){
                if(current.right.value==key)
                    return current;
                else
                    queue.add(current.right);
            }
        }
        return null;
    }
    //获得关键字最大的节点
    public static TreeNode getMaxTreeNode(TreeNode root){
        if(root == null || root.right==null)
            return root;
        if(root.right != null){
            root = getMaxTreeNode(root.right);
        }
        return root;
    }
    //获得关键字最小的节点
    public static TreeNode getMinTreeNode(TreeNode root){
        if(root == null || root.left==null)
            return root;
        if(root.left != null){
            root = getMinTreeNode(root.left);
        }
        return root;
    }
    //打印排序二叉树
    public static void printInOrder(TreeNode root){
        if(root!=null){
            printInOrder(root.left);
            System.out.print(root.value+" ");
            printInOrder(root.right);           
        }
    }
    public static void main(String[] args) throws IOException {
        StreamTokenizer cin = new StreamTokenizer (new BufferedReader(new InputStreamReader(System.in)));
        while(cin.nextToken()!=cin.TT_EOF){
            int n =(int)cin.nval;
            int []input = new int[n];
            for(int i=0;i<n;i++){
                cin.nextToken();
                input[i] = (int)cin.nval;
            }
            TreeNode root = null;
            for(int i=0;i<n;i++){
                root = insertTreeNode(root,input[i]);
            }
            printInOrder(root);
            for(int i=1;i<input.length;i++){
                TreeNode pNpde = serachTreeNode(root,input[i]);
                deleteTreeNode(root,pNpde);
                System.out.print("\n");
                printInOrder(root);
            }
        }
    }
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值