数据结构之二叉树--转载

基础概念  

  二叉树(binary tree)是一棵树,其中每个结点都不能有多于两个儿子。

  二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:

    (1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;

    (2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;

    (3)左、右子树也分别为二叉排序树;

二叉树的遍历

  二叉树的遍历是指从根节点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。二叉树的遍历方式有很多,主要有前序遍历,中序遍历,后序遍历。

前序遍历

  前序遍历的规则是:若二叉树为空,则空操作返回,否则先访问根节点,然后前序遍历左子树,再前序遍历右子树

扫描二维码关注公众号,回复: 6352279 查看本文章

 

中序遍历

   中序遍历的规则是:若树为空,则空操作返回;否则从根节点开始(注意并不是先访问根节点),中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树。可以看到,如果是二叉排序树,中序遍历的结果就是个有序序列。

 

后序遍历

  后序遍历的规则是:若树为空,则空操作返回;然后先遍历左子树,再遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。

 

删除结点

  对于二叉排序树的其他操作,比如插入,遍历等,比较容易理解;而删除操作相对复杂些。对于要删除的结点,有以下三种情况:

    1.叶子结点;

    2.仅有左子树或右子树的结点;

    3.左右子树都有结点;

  对于1(要删除结点为叶子结点)直接删除,即直接解除父节点的引用即可,对于第2种情况(要删除的结点仅有一个儿子),只需用子结点替换掉父节点即可;而对于要删除的结点有两个儿子的情况,比较常用处理逻辑为,在其子树中找寻一个结点来替换,而这个结点我们成为中序后继结点。

 

  可以看到,我们找到的这个用来替换的结点,可以是删除结点的右子树的最小结点(6),也可以是其左子树的最大结点(4),这样可以保证替换后树的整体结构不用发生变化。为什么称为中序后继结点呢?我们来看下这棵树的中序遍历结果 1-2-3-4-5-6-7-8-9。可以很清晰的看到,其实要找的这个结点,可以是结点5的前驱或者后继。

代码实现

package treeDemo;

/**
 * Created by chengxiao on 2017/02/12.
 */
public class BinaryTree {
    //根节点
    private Node root;
    /**
     * 树的结点
     */
    private static class Node{
        //数据域
        private long data;
        //左子结点
        private Node leftChild;
        //右子结点
        private Node rightChild;
        Node(long data){
            this.data = data;
        }
    }

    /**
     * 插入结点
     * @param data
     */
    public void insert(long data){
        Node newNode = new Node(data);
        Node currNode = root;
        Node parentNode;
        //如果是空树
        if(root == null){
            root = newNode;
            return;
        }
        while(true){
            parentNode = currNode;
            //向右搜寻
            if(data > currNode.data){
                currNode = currNode.rightChild;
                if(currNode == null){
                    parentNode.rightChild = newNode;
                    return;
                }
            }else{
                //向左搜寻
                currNode = currNode.leftChild;
                if(currNode == null){
                    parentNode.leftChild = newNode;
                    return;
                }
            }
        }

    }

    /**
     * 前序遍历
     * @param currNode
     */
    public void preOrder(Node currNode){
        if(currNode == null){
            return;
        }
        System.out.print(currNode.data+" ");
        preOrder(currNode.leftChild);
        preOrder(currNode.rightChild);
    }

    /**
     * 中序遍历
     * @param currNode
     */
    public void inOrder(Node currNode){
        if(currNode == null){
            return;
        }
        inOrder(currNode.leftChild);
        System.out.print(currNode.data+" ");
        inOrder(currNode.rightChild);

    }

    /**
     * 后序遍历
     * @param currNode
     */
    public void postOrder(Node currNode){
        if(currNode == null){
            return;
        }
        postOrder(currNode.leftChild);
        postOrder(currNode.rightChild);
        System.out.print(currNode.data+" ");
    }

    /**
     * 查找结点
     * @param data
     * @return
     */
    public Node find(long data){
        Node currNode = root;
        while(currNode!=null){
            if(data>currNode.data){
                currNode = currNode.rightChild;
            }else if(data<currNode.data){
                currNode = currNode.leftChild;
            }else{
                return currNode;
            }
        }
        return null;
    }

    /**
     * 删除结点 分为3种情况
     * 1.叶子结点
     * 2.该节点有一个子节点
     * 3.该节点有二个子节点
     * @param data
     */
    public boolean delete(long data) throws Exception {
        Node curr = root;
        //保持一个父节点的引用
        Node parent = curr;
        //删除结点是左子结点还是右子结点,
        boolean isLeft = true;
        while(curr != null && curr.data!=data){
            parent = curr;
            if(data > curr.data){
                curr = curr.rightChild;
                isLeft = false;
            }else{
                curr = curr.leftChild;
                isLeft = true;
            }
        }
        if(curr==null){
            throw new Exception("要删除的结点不存在");
        }
        //第一种情况,要删除的结点为叶子结点
        if(curr.leftChild == null && curr.rightChild == null){
            if(curr == root){
                root = null;
                return true;
            }
            if(isLeft){
                parent.leftChild = null;
            }else{
                parent.rightChild = null;
            }
        }else if(curr.leftChild == null){
            //第二种情况,要删除的结点有一个子节点且是右子结点
            if(curr == root){
                root = curr.rightChild;
                return true;
            }
            if(isLeft){
                parent.leftChild = curr.rightChild;
            }else{
                parent.rightChild = curr.rightChild;
            }
        }else if(curr.rightChild == null){
            //第二种情况,要删除的结点有一个子节点且是左子结点
            if(curr == root){
                root = curr.leftChild;
                return true;
            }
            if(isLeft){
                parent.leftChild = curr.leftChild;
            }else{
                parent.rightChild = curr.leftChild;
            }
        }else{
            //第三种情况,也是最复杂的一种情况,要删除的结点有两个子节点,需要找寻中序后继结点
            Node succeeder = getSucceeder(curr);
            if(curr == root){
                root = succeeder;
                return  true;
            }
            if(isLeft){
                parent.leftChild = succeeder;
            }else{
                parent.rightChild = succeeder;
            }
            //当后继结点为删除结点的右子结点
            succeeder.leftChild = curr.leftChild;

        }
        return true;
    }
    public Node getSucceeder(Node delNode){
        Node succeeder = delNode;
        Node parent = delNode;
        Node currNode = delNode.rightChild;
        //寻找后继结点
        while(currNode != null){
            parent = succeeder;
            succeeder = currNode;
            currNode = currNode.leftChild;
        }
        //如果后继结点不是要删除结点的右子结点
        if(succeeder != delNode.rightChild){
            parent.leftChild = succeeder.rightChild;
            //将后继结点的左右子结点分别指向要删除结点的左右子节点
            succeeder.leftChild = delNode.leftChild;
            succeeder.rightChild = delNode.rightChild;
        }
        return succeeder;

    }
    public static void main(String []args) throws Exception {
        BinaryTree binaryTree = new BinaryTree();
        //插入操作
        binaryTree.insert(5);
        binaryTree.insert(2);
        binaryTree.insert(8);
        binaryTree.insert(1);
        binaryTree.insert(3);
        binaryTree.insert(6);
        binaryTree.insert(10);
        //前序遍历
        System.out.println("前序遍历:");
        binaryTree.preOrder(binaryTree.root);
        System.out.println();
        //中序遍历
        System.out.println("中序遍历:");
        binaryTree.inOrder(binaryTree.root);
        System.out.println();
        //后序遍历
        System.out.println("后序遍历:");
        binaryTree.postOrder(binaryTree.root);
        System.out.println();
        //查找结点
        Node node = binaryTree.find(10);
        System.out.println("找到结点,其值为:"+node.data);
        //删除结点
        binaryTree.delete(8);
        System.out.print("删除结点8,中序遍历:");
        binaryTree.preOrder(binaryTree.root);
    }
}

二叉树的基本操作
View Code

猜你喜欢

转载自www.cnblogs.com/godyh/p/10977221.html