二叉树

作者: 笨笨哒2018 | 来源:发表于2018-08-23 18:03 被阅读0次

    什么是二叉树?

    二叉树是 n (n >= 0)个结构的有限集合,如果集合为空集(称为空二叉树),或者有一个根节点和两棵互不相交的、分别称为根节点的左子树和右子树的二叉树组成。

    什么是满二叉树?

    在一棵二叉树中,如果所有的分支节点都存在左子树和右子树,并且所有叶子都在同一层面上,这样的二叉树成为满二叉树。


    image.png

    满二叉树的特点

    (1) 满二叉树的叶子只能出现在最下一层,出现在其它层就不可能达成平衡。
    (2) 非叶子节点的度一定是2。
    (3) 在同样深度的热茶树中,满二叉树的节点个数最多,叶子数最多。

    什么是完全二叉树?

    对于一棵具有 n 个节点的二叉树按层序编号,如果编号为 i ( 1<= i <= n)的节点与同样深度的满二叉树中编号为 i 的节点在二叉树中位置完全相同,则这棵二叉树成为完全二叉树。


    image.png

    完全二叉树的特点

    (1) 完全二叉树的叶子节点只能出现在最下面的两层.
    (2) 最下层的叶子一定集中在左部的连续位置.
    (3) 倒数二层,若有叶子节点,则一定在右部的连续位置
    (4) 不存在只有右子树的情况.
    (5) 同样结点书的二叉树,完全二叉树的深度最小.

    二叉搜索树

    如果我们给二叉树加一个额外的条件,就可以得到一种被称作二叉搜索树(binary search tree)的特殊二叉树。
    二叉搜索树要求:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。


    image.png

    定义二叉树的节点类

    public class Node {
        int data;   //节点数据
        Node leftChild; //左子节点的引用
        Node rightChild; //右子节点的引用
        boolean isDelete;//表示节点是否被删除
    
        public Node(int data){
            this.data = data;
        }
        //打印节点内容
        public void display(){
            System.out.println(data);
        }
    
    }
    
    

    二叉树的具体方法

    public interface Tree {
        //查找节点
        public Node find(int key);
        //插入新节点
        public boolean insert(int data);
    
        //中序遍历
        public void infixOrder(Node current);
        //前序遍历
        public void preOrder(Node current);
        //后序遍历
        public void postOrder(Node current);
    
        //查找最大值
        public Node findMax();
        //查找最小值
        public Node findMin();
    
        //删除节点
        public boolean delete(int key);
    
    }
    

    查找节点

    查找某个节点,我们必须从根节点开始遍历。
      ①、查找值比当前节点值大,则搜索右子树;
      ②、查找值等于当前节点值,停止搜索(终止条件);
      ③、查找值小于当前节点值,则搜索左子树;

    //查找节点
        public Node find(int key) {
            Node current = root;
            while(current != null){
                if(current.data > key){//当前值比查找值大,搜索左子树
                    current = current.leftChild;
                }else if(current.data < key){//当前值比查找值小,搜索右子树
                    current = current.rightChild;
                }else{
                    return current;
                }
            }
            return null;//遍历完整个树没找到,返回null
        }
     
    

    用变量current来保存当前查找的节点,参数key是要查找的值,刚开始查找将根节点赋值到current。接在在while循环中,将要查找的值和current保存的节点进行对比。如果key小于当前节点,则搜索当前节点的左子节点,如果大于,则搜索右子节点,如果等于,则直接返回节点信息。当整个树遍历完全,即current == null,那么说明没找到查找值,返回null。

    插入节点

    要插入节点,必须先找到插入的位置。与查找操作相似,由于二叉搜索树的特殊性,待插入的节点也需要从根节点开始进行比较,小于根节点则与根节点左子树比较,反之则与右子树比较,直到左子树为空或右子树为空,则插入到相应为空的位置,在比较的过程中要注意保存父节点的信息 及 待插入的位置是父节点的左子树还是右子树,才能插入到正确的位置。

    //插入节点
        public boolean insert(int data) {
            Node newNode = new Node(data);
            if(root == null){//当前树为空树,没有任何节点
                root = newNode;
                return true;
            }else{
                Node current = root;
                Node parentNode = null;
                while(current != null){
                    parentNode = current;
                    if(current.data > data){//当前值比插入值大,搜索左子节点
                        current = current.leftChild;
                        if(current == null){//左子节点为空,直接将新值插入到该节点
                            parentNode.leftChild = newNode;
                            return true;
                        }
                    }else{
                        current = current.rightChild;
                        if(current == null){//右子节点为空,直接将新值插入到该节点
                            parentNode.rightChild = newNode;
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    

    遍历树

    遍历树是根据一种特定的顺序访问树的每一个节点。比较常用的有前序遍历,中序遍历和后序遍历。而二叉搜索树最常用的是中序遍历。
      ①、前序遍历:根节点——》左子树——》右子树
      ②、中序遍历:左子树——》根节点——》右子树
      ③、后序遍历:左子树——》右子树——》根节点


    image.png

    二叉树的具体实现

    public class BinaryTree implements Tree {
        //表示根节点
        private Node root;
     
        //查找节点
        public Node find(int key) {
            Node current = root;
            while(current != null){
                if(current.data > key){//当前值比查找值大,搜索左子树
                    current = current.leftChild;
                }else if(current.data < key){//当前值比查找值小,搜索右子树
                    current = current.rightChild;
                }else{
                    return current;
                }
            }
            return null;//遍历完整个树没找到,返回null
        }
     
        //插入节点
        public boolean insert(int data) {
            Node newNode = new Node(data);
            if(root == null){//当前树为空树,没有任何节点
                root = newNode;
                return true;
            }else{
                Node current = root;
                Node parentNode = null;
                while(current != null){
                    parentNode = current;
                    if(current.data > data){//当前值比插入值大,搜索左子节点
                        current = current.leftChild;
                        if(current == null){//左子节点为空,直接将新值插入到该节点
                            parentNode.leftChild = newNode;
                            return true;
                        }
                    }else{
                        current = current.rightChild;
                        if(current == null){//右子节点为空,直接将新值插入到该节点
                            parentNode.rightChild = newNode;
                            return true;
                        }
                    }
                }
            }
            return false;
        }
         
        //中序遍历
        public void infixOrder(Node current){
            if(current != null){
                infixOrder(current.leftChild);
                System.out.print(current.data+" ");
                infixOrder(current.rightChild);
            }
        }
         
        //前序遍历
        public void preOrder(Node current){
            if(current != null){
                System.out.print(current.data+" ");
                infixOrder(current.leftChild);
                infixOrder(current.rightChild);
            }
        }
         
        //后序遍历
        public void postOrder(Node current){
            if(current != null){
                infixOrder(current.leftChild);
                infixOrder(current.rightChild);
                System.out.print(current.data+" ");
            }
        }
        //找到最大值
        public Node findMax(){
            Node current = root;
            Node maxNode = current;
            while(current != null){
                maxNode = current;
                current = current.rightChild;
            }
            return maxNode;
        }
        //找到最小值
        public Node findMin(){
            Node current = root;
            Node minNode = current;
            while(current != null){
                minNode = current;
                current = current.leftChild;
            }
            return minNode;
        }
         
        @Override
        public boolean delete(int key) {
            Node current = root;
            Node parent = root;
            boolean isLeftChild = false;
            //查找删除值,找不到直接返回false
            while(current.data != key){
                parent = current;
                if(current.data > key){
                    isLeftChild = true;
                    current = current.leftChild;
                }else{
                    isLeftChild = false;
                    current = current.rightChild;
                }
                if(current == null){
                    return false;
                }
            }
            //如果当前节点没有子节点
            if(current.leftChild == null && current.rightChild == null){
                if(current == root){
                    root = null;
                }else if(isLeftChild){
                    parent.leftChild = null;
                }else{
                    parent.rightChild = null;
                }
                return true;
                 
                //当前节点有一个子节点,右子节点
            }else if(current.leftChild == null && current.rightChild != null){
                if(current == root){
                    root = current.rightChild;
                }else if(isLeftChild){
                    parent.leftChild = current.rightChild;
                }else{
                    parent.rightChild = current.rightChild;
                }
                return true;
                //当前节点有一个子节点,左子节点
            }else if(current.leftChild != null && current.rightChild == null){
                if(current == root){
                    root = current.leftChild;
                }else if(isLeftChild){
                    parent.leftChild = current.leftChild;
                }else{
                    parent.rightChild = current.leftChild;
                }
                return true;
            }else{
                //当前节点存在两个子节点
                Node successor = getSuccessor(current);
                if(current == root){
                    successor = root;
                }else if(isLeftChild){
                    parent.leftChild = successor;
                }else{
                    parent.rightChild = successor;
                }
                successor.leftChild = current.leftChild;
            }
            return false;
             
        }
     
        public Node getSuccessor(Node delNode){
            Node successorParent = delNode;
            Node successor = delNode;
            Node current = delNode.rightChild;
            while(current != null){
                successorParent = successor;
                successor = current;
                current = current.leftChild;
            }
            //后继节点不是删除节点的右子节点,将后继节点替换删除节点
            if(successor != delNode.rightChild){
                successorParent.leftChild = successor.rightChild;
                successor.rightChild = delNode.rightChild;
            }
             
            return successor;
        }
         
        public static void main(String[] args) {
            BinaryTree bt = new BinaryTree();
            bt.insert(50);
            bt.insert(20);
            bt.insert(80);
            bt.insert(10);
            bt.insert(30);
            bt.insert(60);
            bt.insert(90);
            bt.insert(25);
            bt.insert(85);
            bt.insert(100);
            bt.delete(10);//删除没有子节点的节点
            bt.delete(30);//删除有一个子节点的节点
            bt.delete(80);//删除有两个子节点的节点
            System.out.println(bt.findMax().data);
            System.out.println(bt.findMin().data);
            System.out.println(bt.find(100));
            System.out.println(bt.find(200));
             
        }
     
    }
    

    参考资料
    https://www.cnblogs.com/ysocean/p/8032642.html#_label9

    相关文章

      网友评论

          本文标题:二叉树

          本文链接:https://www.haomeiwen.com/subject/lqvmiftx.html