二叉树及其遍历

基本概念

二叉树:递归定义。二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。

五种形态:1.空树;2.仅有一个根结点的二叉树;3.仅有左子树而右子树为空的二叉树;4.仅有右子树而左子树为空的二叉树;5.左、右子树均非空的二叉树

满二叉树:除叶子结点外的所有结点均有两个子结点。特点:节点数达到最大值时所有叶子结点必须在同一层上总节点数一定是奇数

完全二叉树:若树有K层,则0~K-1层结点都完全填满,在第K层上如果不是满的,则只缺少右边的若干结点。至于和满二叉树区别,从定义可以看出!(设某个结点为序号为i, 如果它有左子树,那么左子树的位置是2i+1,如果有右子树,右子树的位置是2i+2,如果有父节点,父节点的位置是floor((i-1)/2)

哈夫曼树:为满二叉树。应用:哈夫曼编码。定义:一种带权路径长度最短(WPL)的二叉树,也称为最优二叉树。,一棵二叉树要使其WPL值最小,必须使权值越大的叶子结点越靠近根结点,而权值越小的叶子结点越远离根结点。
这里写图片描述

一些基本性质:在二叉树的第K层上,最多有2^(k-1)个节点;层数为K的二叉树最多有2^K-1个节点,最少有K个节点;对于一棵非空的二叉树,如果叶子节点数为n0,度数为2的节点数为n2,则有n0=n2+1

二叉树遍历

遍历是二叉树最主要和最基本的方法,通常有层次遍历,前、中、后序遍历!需要掌握递归和非递归算法!

  1. 定义一个二叉树节点类
package xianggen.tree;
/**
 * 树节点类,泛型
 * TreeNode.java
 * @author xianggen
 * @date 2016年8月11日 下午2:37:23
 * @param <E>
 */
public class TreeNode<E> {
    private E value;
    private TreeNode<E> leftChild;
    private TreeNode<E> rightChild;

    /**
     * 无参构造函数
     */
    public TreeNode(){
    }

    /**
     * 带value构造函数
     * @param value
     */
    public TreeNode(E value){
        this.value=value;
        leftChild=null;
        rightChild=null;
    }

    /**
     * 三个参数的set和get方法
     * @return
     */

    public E getValue() {
        return value;
    }

    public void setValue(E value) {
        this.value = value;
    }

    public TreeNode<E> getLeftChild() {
        return leftChild;
    }

    public void setLeftChild(TreeNode<E> leftChild) {
        this.leftChild = leftChild;
    }

    public TreeNode<E> getRightChild() {
        return rightChild;
    }

    public void setRightChild(TreeNode<E> rightChild) {
        this.rightChild = rightChild;
    }

}
  1. 二叉树类,各种遍历算法
package xianggen.tree;

import java.util.LinkedList;
import java.util.Stack;

/**
 * 二叉树,前、中后序遍历 BinaryTree.java
 * 
 * @author xianggen
 * @date 2016年8月11日 下午2:38:42
 */
public class BinaryTree {
    private TreeNode<Integer> root;

    public BinaryTree() {
        root = new TreeNode<Integer>();
    }

    /**
     * 带参构造函数
     * 
     * @param root
     */
    public BinaryTree(TreeNode<Integer> root) {
        this.root = root;
    }

    /**
     * 获取二叉树根节点
     * 
     * @return
     */
    public TreeNode<Integer> getRoot() {
        return root;
    }

    /**
     * 数组构造器,利用给定数组创建一棵二叉树
     * @param arr
     */
    public BinaryTree(int[] arr) {
        boolean isLeft = true;
        int len = arr.length;
        if (len == 0)
            return;
        LinkedList<TreeNode<Integer>> queue = new LinkedList<TreeNode<Integer>>();
        root = new TreeNode<Integer>(arr[0]); // 数组第一个元素作为根节点
        queue.addLast(root);
        TreeNode<Integer> parentNode,currentNode;
        for (int i = 1; i < len; i++) {
            currentNode = new TreeNode<Integer>(arr[i]);
            queue.addLast(currentNode);
            if (isLeft) {
                parentNode = queue.getFirst();
            } else {
                parentNode = queue.removeFirst();
            }

            if (isLeft) {
                parentNode.setLeftChild(currentNode);
                isLeft = false;
            } else {
                parentNode.setRightChild(currentNode);
                isLeft = true;
            }
        }
    }

    /**
     * 访问节点,获取value
     * 
     * @param treeNode
     */
    public void visitTreeNode(TreeNode<Integer> treeNode) {
        System.out.print(treeNode.getValue() + " ");
    }

    /**
     * 递归获取二叉树深度
     * 
     * @param treeNode
     * @return
     */
    public int getTreeDepthByRecursion(TreeNode<Integer> treeNode) {
        if (treeNode == null) {
            return 0;
        }
        TreeNode<Integer> leftChild = treeNode.getLeftChild();
        TreeNode<Integer> rightChild = treeNode.getRightChild();
        return Math.max(getTreeDepthByRecursion(leftChild),
                getTreeDepthByRecursion(rightChild)) + 1;

    }

    /**
     * 层次遍历--非递归
     * 采用一个队列作为辅助!!先进先出!
     */
    public void TraverseByLevel() {
        LinkedList<TreeNode<Integer>> queue = new LinkedList<TreeNode<Integer>>();
        if (root != null) {
            queue.add(root);
        }
        TreeNode<Integer> currentNode;
        while (!queue.isEmpty()) {
            currentNode = queue.removeFirst();
            if (currentNode.getLeftChild() != null) {
                queue.addLast(currentNode.getLeftChild());
            }
            if (currentNode.getRightChild() != null) {
                queue.addLast(currentNode.getRightChild());
            }
            visitTreeNode(currentNode);
        }
        System.out.println();
    }

    /**
     * 前序遍历--递归
     * 二叉树前序递归遍历!
     * 先访问根结点然后遍历左子树,最后遍历右子树
     * 在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树。
     * @param treeNode
     */
    public void TraverseByPreOrderRecursion(TreeNode<Integer> treeNode) {
        if (treeNode != null) {   //在此处做非空判断后就不需要在下面子节点判断非空
            visitTreeNode(treeNode);
            TraverseByPreOrderRecursion(treeNode.getLeftChild());
            TraverseByPreOrderRecursion(treeNode.getRightChild());
        }
        // System.out.println();
    }

    /**
     * 前序遍历--非递归
     * 非递归前序(先根)遍历二叉树
     * 利用栈先进后出的特点,先将右子节点压栈
     * @param treeNode
     */
    public void TraverseByPreOrder(TreeNode<Integer> treeNode){
        Stack<TreeNode<Integer>> stack=new Stack<TreeNode<Integer>>();
        TreeNode<Integer> currentNode=treeNode;
        if(currentNode!=null){
            stack.push(currentNode);
            while(!stack.isEmpty()){
                currentNode=stack.pop();
                visitTreeNode(currentNode);
                //关键,根据栈先进后出的特点,先讲右子节点压栈
                if(currentNode.getRightChild()!=null){
                    stack.push(currentNode.getRightChild());
                }
                if(currentNode.getLeftChild()!=null){
                    stack.push(currentNode.getLeftChild());
                }
            }
        }
    }

    /**
     * 中序遍历--递归
     * 首先遍历左子树,然后访问根结点,最后遍历右子树
     * 在遍历左、右子树时,仍然先遍历左子树,再访问根结点,最后遍历右子树
     * @param treeNode
     */
    public void TraverseByInOrderRecursion(TreeNode<Integer> treeNode) {
        if(treeNode!=null){
            TraverseByInOrderRecursion(treeNode.getLeftChild());
            visitTreeNode(treeNode);
            TraverseByInOrderRecursion(treeNode.getRightChild());
        }
    }

    /**
     * 中序遍历--非递归
     * 首先遍历左子树,然后访问根结点,最后遍历右子树
     * 在遍历左、右子树时,仍然先遍历左子树,再访问根结点,最后遍历右子树
     * @param treeNode
     */
    public void TraverseByInOrder(TreeNode<Integer> treeNode){
        Stack<TreeNode<Integer>> stack=new Stack<TreeNode<Integer>>();
        TreeNode<Integer> currentNode=treeNode;
        //注意循环条件
        while(currentNode!=null||!stack.isEmpty()){
            //这一步非常关键,找到当前节点的左孩子,一直循环,直至不能找到左孩子为止!!
            while(currentNode!=null){
                stack.push(currentNode);
                currentNode=currentNode.getLeftChild();
            }
            if(!stack.isEmpty()){
                currentNode=stack.pop();
                visitTreeNode(currentNode);
                currentNode=currentNode.getRightChild();
            }
        }
    }

    /**
     * 后序遍历--递归
     * 先遍历左子树,然后遍历右子树,最后访问根结点
     * 在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点
     * @param treeNode
     */
    public void TraverseByPostOrderRecursion(TreeNode<Integer> treeNode){
        if(treeNode!=null){
            TraverseByPostOrderRecursion(treeNode.getLeftChild());
            TraverseByPostOrderRecursion(treeNode.getRightChild());
            visitTreeNode(treeNode);
        }
    }

    /**
     * 后序遍历--非递归_1(单栈)
     * 先遍历左子树,然后遍历右子树,最后访问根结点
     * 在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点
     * @param treeNode
     */
    public void TraverseByPostOrder_1(TreeNode<Integer> treeNode){
        Stack<TreeNode<Integer>> stack=new Stack<TreeNode<Integer>>();
        TreeNode<Integer> currentNode=treeNode,previousNode=treeNode;
        while(currentNode!=null||!stack.isEmpty()){
            while(currentNode!=null){
                stack.push(currentNode);
                currentNode=currentNode.getLeftChild();
            }
            if(!stack.isEmpty()){
                TreeNode<Integer> tempNode=stack.peek().getRightChild();
                //这一步非常重要,previousNode==tempNode表示右子树已访问
                if(tempNode==null||previousNode==tempNode){
                    currentNode=stack.pop();
                    visitTreeNode(currentNode);
                    previousNode=currentNode;
                    currentNode=null;
                }else{
                    currentNode=tempNode;
                }
            }
        }
    }

    /**
     * 后序遍历--非递归_2(双栈)
     * 先遍历左子树,然后遍历右子树,最后访问根结点
     * 在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点
     * @param treeNode
     */
    public void TraverseByPostOrder_2(TreeNode<Integer> treeNode){
        Stack<TreeNode<Integer>> leftStack=new Stack<TreeNode<Integer>>();
        Stack<TreeNode<Integer>> rightStack=new Stack<TreeNode<Integer>>();
        TreeNode<Integer> currentNode=treeNode,rightNode;
        do{
            while(currentNode!=null){
                rightNode=currentNode.getRightChild();
                leftStack.push(currentNode);
                rightStack.push(rightNode);
                currentNode=currentNode.getLeftChild();
            }
            currentNode=leftStack.pop();
            rightNode=rightStack.pop();
            if(rightNode==null){
                visitTreeNode(currentNode);
            }else{
                leftStack.push(currentNode);
                rightStack.push(null);
            }
            currentNode=rightNode;
        }while(!leftStack.isEmpty()||!rightStack.isEmpty());
    }


    public static void main(String[] args) {
        int[] arr = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
        BinaryTree bt = new BinaryTree(arr);
        System.out.println("根节点值:"+bt.getRoot().getValue());
        System.out.println("树深度值:"+bt.getTreeDepthByRecursion(bt.getRoot()));
        System.out.println("------层次遍历------");
        bt.TraverseByLevel();
        System.out.println("------前序遍历------");
        bt.TraverseByPreOrderRecursion(bt.getRoot());
        System.out.println();
        bt.TraverseByPreOrder(bt.getRoot());
        System.out.println();
        System.out.println("------中序遍历------");
        bt.TraverseByInOrderRecursion(bt.getRoot());
        System.out.println();
        bt.TraverseByInOrder(bt.getRoot());
        System.out.println();
        System.out.println("------后序遍历------");
        bt.TraverseByPostOrderRecursion(bt.getRoot());
        System.out.println();
        bt.TraverseByPostOrder_1(bt.getRoot());
        System.out.println();
        bt.TraverseByPostOrder_2(bt.getRoot());
        System.out.println();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值