二叉查找树的结构特点:

1.新插入的节点的一定是作为某个叶子节点的子节点。

2.某个节点左侧的所有节点都小于它,右侧的所有节点都大于它。

3.找后继(前驱)节点,一直往上找,找到右拐(左拐)后的那个节点

package study;

import java.util.Comparator;

/**
 * Created by 刘逗逼 on 2016/2/25.
 */
public class BSTree <T extends Comparable<T>>{      //树,只有根节点,然后根据根节点去操作
    private BNode<T> root;
    public class BNode<T extends Comparable<T>>{         //节点,有左右孩子
        private T key;
        private BNode<T> left;
        private BNode<T> right;
        private BNode<T> parent;
        public BNode(T key,BNode left,BNode right,BNode parent){
            this.key=key;
            this.left=left;
            this.right=right;
            this.parent=parent;
        }
        public T getKey(){
            return key;
        }
        public String toString(){
            return "key is "+key;
        }
    }
    public BSTree(){
        root=null;
    }
    public void preOrder(BNode<T> tree){              //先序遍历
        if(tree!=null){
            System.out.println(tree.toString());
            preOrder(tree.left);
            preOrder(tree.right);
        }
    }
    public void preOrder(){               //默认为对整颗树进行排序
        preOrder(root);
    }
    public void inOrder(BNode<T> tree){
        if (tree!=null){
            inOrder(tree.left);
            System.out.println(tree.toString());
            inOrder(tree.right);
        }
    }
    public void inOrder(){
        inOrder(root);
    }
    public void postOrder(BNode<T> tree){
        if (tree!=null){
            postOrder(tree.left);
            postOrder(tree.right);
            System.out.println(tree.toString());
        }
    }
    public void postOrder(){
        postOrder(root);
    }
    //递归查找二叉树中键值为key的节点
    public BNode<T> search(BNode<T> tree,T key){
        if(tree==null)return tree;              //此为树为空或找不到键值为key的节点的情况
        int cmp=key.compareTo(tree.getKey());
        if(cmp<0){
            return search(tree.left,key);
        }else if(cmp>0){
            return search(tree.right,key);
        }else return tree;
    }
    public BNode<T> search(T key){
        return search(root,key);
    }
    //(非递归实现)查找二叉树中键值为key的节点     非递归就是循环
    public BNode<T> iterativeSearch(BNode<T> tree, T key){
       while (tree!=null){
           int cmp=key.compareTo(tree.getKey());
           if(cmp<0){
               tree=tree.left;
           }else if(cmp>0){
               tree=tree.right;
           }else return tree;
       }
        return tree;
    }
    public BNode<T> iterativeSearch(T key){
        return iterativeSearch(root,key);
    }
    //查找最小结点:返回tree为根结点的二叉树的最小结点。
    public BNode<T> minnum(BNode<T> tree){
        if(tree==null)return tree;
        while (tree.left!=null){
            tree=tree.left;
        }
        return tree;
    }
    public BNode<T> minnum(){
        return minnum(root);
    }
    //查找最大结点:返回tree为根结点的二叉树的最大结点。
    public BNode<T> maxnum(BNode<T> tree){
        if(tree==null)return tree;
        while (tree.right!=null){
            tree=tree.right;
        }
        return tree;
    }
    public BNode<T> maxnum(){
        return maxnum(root);
    }
    //找结点tree的前驱结点。即查找二叉树中数据值小于该结点的最大结点。
    public BNode<T> predecessor(BNode<T> tree) {
        // 如果tree存在左孩子,则tree的前驱结点为 tree以其左孩子为根的子树的最大结点。
        if (tree.left != null)
            return maxnum(tree.left);

        // 如果tree没有左孩子。则tree有以下两种可能:
        // 1. tree是右孩子,则tree的前驱结点为它的父结点。
        // 2. tree是左孩子,则查找tree的最低的父结点,并且该父结点要具有右孩子,即tree在父节点的左侧才行,找到的这个
        // 最低的父结点就是tree的前驱结点。
        BNode<T> parent = tree.parent;
        while ((parent!=null) && (tree==parent.left)) {               //当parent.left=tree退出循环时说明tree在此父节点的左侧,而且在此前循环所经过
            tree = parent;                                            // 的节点都小于tree,因为tree一直在这些节点的右边说明tree比他们大
            parent = parent.parent;
        }
        return parent;
    }
    //找结点tree的后继结点。即,查找二叉树中数据值大于该结点的最小结点。
    public BNode<T> successor(BNode<T> tree){
        // 如果tree存在右孩子,则tree的后继结点为 以其右孩子为根的子树的最小结点。
        if(tree.right!=null){
            return minnum(tree.right);
        }
        // 如果tree没有右孩子。则tree有以下两种可能:
        // 1.tree是左孩子,根据二叉树的规律,其父结点即为后继节点。
        // 2.tree是右孩子,则查找tree的最低的父结点,并且该父结点要具有左孩子,即tree在父节点的左侧才行,
        // 找到的这个最低的父结点就是tree的后继结点。
        BNode<T> parent=tree.parent;
        while (parent!=null&&parent.right==tree){      //当parent.left=tree退出循环时说明tree在此父节点的左侧,而且在此前循环所经过
                                                       // 的节点都小于tree,因为tree一直在这些节点的右边说明tree比他们大
            tree=parent;
            parent=parent.parent;
        }
        return parent;
    }
    public void insert(BSTree<T> tree,BNode<T> node){
        int cmp;
        BNode<T> child=tree.root;
        BNode<T> parent=null;
        //找到插入位置,肯定是位于一个父节点和子节点之间
        while (child!=null){
            parent=child;            //父节点是被抛弃的,关键是看下一步是往子节点的左边还是右边走
            cmp=node.getKey().compareTo(child.getKey());
            if(cmp<0){
                child=child.left;
            }else{
                child=child.right;
            }
        }
        //最后的插入位置一定是作为某个叶子节点的子节点
        node.parent=parent;
        if(parent==null){         //树为空
            tree.root=node;
        }else {
            cmp=node.getKey().compareTo(parent.getKey());
            if(cmp<0){   //左节点
                parent.left=node;
            }else {
                parent.right=node;
            }
        }
    }
    //新建结点(key),并将其插入到二叉树中
    public void insert(T key){
        BNode<T> node=new BNode<T>(key,null,null,null);
        if (node!=null) {
            insert(this,node);
        }
    }
    //删除结点node
    //若要删除任意节点,需要考虑如下三种情况:
    // (1)需要删除的节点下并没有其他子节点。                   删了没影响
    // (2)需要删除的节点下有一个子节点(左或右)。             让被删节点的父节点和子节点建立关系,类似于链表的删除节点
    // (3)需要删除的节点下有两个子节点(既左右节点都存在)。   让后继或前继节点代替被删节点,然后删除后继节点

    //递归实现
    public boolean remove(T key){
        BNode<T> goal= search(key);
        if (goal != null){
            if (doRemove(goal)){
                goal=null;
                return true;
            }
        }
        return false;
    }
    public boolean doRemove(BNode<T> goal){
        BNode<T> replaceNode=null;               //用来替代被删除节点的
        if(goal!=null){
            boolean isRoot=goal==root;                      //判断是否被删除的是根节点
            if(goal.left==null&&goal.right==null){          //没有子节点,是叶子节点
                if(isRoot){
                    root=null;
                    return true;
                }
                if(goal==goal.parent.left){                 //如果被删除的节点是左节点的话
                    goal.parent.left=null;
                    return true;
                }else {
                    goal.parent.right=null;
                    return true;
                }
            }else if(goal.left!=null&&goal.right!=null){   //如果被删除的节点有两个子节点的话
                replaceNode=successor(goal);
                if(replaceNode==null){
                    replaceNode=predecessor(goal);        //如果有后继节点的话否则用前继节点
                }
                goal.key=replaceNode.key;                 //替换并递归删除替代的节点
                return doRemove(replaceNode);
            }else {                                    //如果只有一个子节点
                BNode<T> needLinkedNode=null;
                if(goal.left==null&&goal.right!=null){                       //判断是左还是右节点是需要连接的节点
                    needLinkedNode=goal.right;
                }else {
                    needLinkedNode=goal.left;
                }
                if(isRoot){
                    root=needLinkedNode;
                }
                if(goal==goal.parent.left){                      //判断需要连接的节点连到左边还是右边
                    goal.parent.left=needLinkedNode;
                    needLinkedNode.parent=goal.parent.left;
                }else {
                    goal.parent.right=needLinkedNode;
                }
                return true;
            }
        }else return false;
    }

    //非递归实现
    public boolean iterativeRemove(BSTree<T> tree,T key){
        BNode<T> node=search(key);
        if(node==null)return false;    //不存在该节点
        BNode<T> child=null;
        BNode<T> parent=null;
        if(node.left==null||node.right==null){    //若只有一边
            parent=node;
        }else {
            parent=successor(node);               //找到其后继节点
        }
        //若它只有一边的话就可以确定     若是有两边此时parent为叶子节点child也为空,不影响
        if(parent.left!=null){
            child=parent.left;
        }else {
            child=parent.right;
        }
        if(child!=null){                          //对应被删除的节点或后继节点不是叶子节点的情况,否则没有child
            child.parent=parent.parent;           //使要替换被删除节点的节点的父节点指向被删除节点的父节点
        }

        if(parent.parent==null){                  //对应树只有这个节点或被删除的是根节点
            tree.root=child;
        }else if(parent==parent.parent.left){     //被删除的节点是左节点
            parent.parent.left=child;
        }else parent.parent.right=child;

        if(parent!=node){          //对应找到后继节点的情况
            node.key=parent.key;
        }
        if(search(key)==null){  //删除成功
            return true;
        }else return false;
    }
    /*
 * 打印"二叉查找树"
 *key和direction都是为了知道上个节点和其与当前节点的关系
 * key        -- 节点的键值
 * direction  --  0,表示该节点是根节点;
 *               -1,表示该节点是它的父结点的左孩子;
 *                1,表示该节点是它的父结点的右孩子。
 */
    public void print(BNode<T> tree,T key,int direction){
        if(tree!=null){                   //先序打印
            if(direction==0){
                System.out.println(tree.key+" is root");
            }else {
                String relationship=direction==1?"right child":"left child";
                System.out.println(tree.key+" is "+key+"`s "+relationship);
            }
            print(tree.left,tree.key,-1);
            print(tree.right,tree.key,1);
        }
    }
    public void print(){
        print(root,root.key,0);
    }


    /*
 * 销毁二叉树,必须从叶子节点开始清空,只能是后序,否则太麻烦
 */
   public void destroy(BNode<T> tree){
       if(tree==null){
           return;
       }
       destroy(tree.left);
       destroy(tree.right);
       tree=null;
   }

    public void clear() {
        destroy(root);
        root = null;
    }
}

 

posted on 2016-02-25 22:54  一个人的合唱  阅读(168)  评论(0编辑  收藏  举报