数据结构与算法学习记录--二叉树的创建,递归遍历,非递归遍历的实现

package test;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Stack;

public class BinaryTree {
    /**
     * 用内部类申明结点结构
     *
     */
    class Node{
        int value = 0;
        Node lchild = null;
        Node rchild = null;
        Node(int value){
            this.value = value;
        }
    }

    /**
     * 先序创建二叉树
     * i为数组下标
     */
    static int i = 0;
    Node createTree(int[] input) {
        Node node= null;
        if(input[i]<0)
            return node;
            node = new Node(input[i]);
            i++;
            node.lchild = createTree( input);
            i++;
            node.rchild = createTree(input);
        return node;
    }

    /**
     * 递归方式实现先序遍历
     * @param root
     * @param result
     */
    void preOrder(Node root,ArrayList<Node> result){
        if(root == null)
            return ;
        result.add(root);
        preOrder(root.lchild,result);
        preOrder(root.rchild,result);
    }

    /**
     * 递归方式实现中序遍历
     * @param root
     * @param result
     */
    void midOrder(Node root,ArrayList<Node> result) {
        if(root == null)
            return ;
        midOrder(root.lchild, result);
        result.add(root);
        midOrder(root.rchild, result);
    }

    /**
     * 递归方式实现后序遍历
     * @param root
     * @param result
     */
    void postOrder(Node root,ArrayList<Node> result) {
        if(root == null)
            return ;
        postOrder(root.lchild,result);
        postOrder(root.rchild,result);
        result.add(root);
    }

    /**
     * 迭代方式实现先序遍历。
     * 因为递归就是用到栈的原理,所以所有递归式子都可以用栈来表示,遍历一定用到了栈。
     * 1.根入栈
     * 2.栈顶元素出栈,右结点入栈,左节点入栈。循环该步骤
     * @param root
     * @return
     */
    ArrayList<Node> preOrder(Node root) {
        Stack<Node> stack = new Stack<Node>();
        ArrayList<Node> list = new ArrayList<Node>();
        if(root == null) {
            return list;
        }
        stack.push(root);
        while(!stack.isEmpty()) {
            Node node = stack.pop();
            list.add(node);
            if(node.rchild!= null)
            stack.push(node.rchild);
            if(node.lchild!=null)
            stack.push(node.lchild);
        }
        return list;
    }

    /**
     * 迭代方式实现中序遍历。
     * 先将所有左节点压栈,左节点为空时,说明已经到了该树枝的尽头,则栈顶元素出栈,再去遍历该结点的右子树
     * @param root
     * @return
     */
    ArrayList<Node> midOrder(Node root){
        ArrayList<Node> list = new ArrayList<Node>();
        Stack<Node> stack = new Stack<Node>();
        Node node = root;
        if(root == null)
            return list;
        while(!stack.isEmpty() || node!=null) {
            while(node!=null) {
                stack.push(node);
                node = node.lchild;
            }
            node = stack.pop();
            list.add(node);
            node = node.rchild;
        }
        return list;
    }
    /**
     * 迭代实现后序遍历
     * 两个重要的变量:刚刚打印完的结点print和栈顶结点top(即将要打印的结点),初始值print为根节点,top为null
     * 1.先判断栈顶结点的左子树是否被遍历,遍历原理是:判断print是否为栈顶节点的左孩子或者右孩子结点
     * 2.再判断栈顶结点的右子树是否被遍历,遍历原理是:判断print是否为栈顶节点的右孩子结点
     * 3.若左右子树都被遍历了,则栈顶结点出栈,打印该该节点。
     * 找到top和print结点的关系
     * @param root
     * @return
     */
     /***以下是自己第一次根据该思路写的代码,繁琐,理解较简单***/
     public ArrayList<Node> postOrder(Node root){
        Stack<Node> stack = new Stack<>();
        ArrayList<Node> list = new ArrayList<>();
        Node print = root;
        Node top = null;
        stack.push(root);
        while(!stack.isEmpty()) {
            top = stack.peek();
            if(print == top.lchild || print == top.rchild) {
//              若是左子树
                if(print == top.lchild) {
                    print = top.rchild;
                    if(top.rchild!=null)
                        stack.push(print);
//                  若是右子树
                }else {
                    list.add(top);
                    print = top;
                    stack.pop();

                }
//              若都不是,则要去遍历左右子树
            }else {
                if(top.rchild!=null) {
                    stack.push(top.rchild);
                    print = top.rchild;
                }
                if(top.lchild!=null) {
                    stack.push(top.lchild);
                    print = top.lchild;
                }
                if(top.rchild == null && top.lchild == null)
                {
                    list.add(top);
                    print = top;
                    stack.pop();
                }
            }

        }
        return list;
    }
    注:最开始写的以下部分是错的.我认为当top结点没有左右子树时,即左右子树都为null时,此时可以令刚打印结点print为右子树结点,但是此时左右子树都为null,所以print的指向的混乱的,并且也没有体现print为刚打印的结点。所以对于自己定义的结点,应该清晰的知道它的作用,并发挥出其作用,不可胡乱使用,造成变量使用混乱。
    if(top.rchild == null && top.lchild == null)
        print = top.rchild;
     /**以下是参考给出的代码,非常简化***/
    ArrayList<Node> postOrder(Node root){
        ArrayList<Node> list = new ArrayList<>();
        Stack<Node> stack = new Stack<>();
        Node top = null;
        Node print = root;
        stack.push(root);
        while(!stack.isEmpty()) {
        //先获取栈顶元素,为了判断栈顶元素是否可以被打印,要执行后面的判断语句
            top = stack.peek();
        //判断栈顶元素的左子树是否被打印了
            if(top.lchild!= null && top.lchild != print && top.rchild !=print) {
                stack.push(top.lchild);
                top = stack.peek();
            }
        //判断栈顶元素的右子树是否被打印了
            else if(top.rchild!=null && top.rchild!= print) {
                stack.push(top.rchild);
                top = stack.peek();
            }
        //左子树右子树都被打印了,则打印栈顶元素
            else
            {
                print = stack.pop();
                list.add(print);
            }

        }
        return list;
    }
    /*
以上两种方法的区别在于对判断当前结点的左右子树是否为打印结点。第一种繁琐的方法是判断当为打印结点时,进行操作,需要考虑各种情况下的操作方法;第二种简单的方法是判断不为打印结点时,进行操作,只有一种操作方法。两种方法是相反的。需要多思考选择最简单的方法。
    */


    public static void main(String[] args) {
        BinaryTree bt = new BinaryTree();
        //测试数据
        int[] array = {3,5,-1,9,-1,-1,7,11,-1,-1,13,-1,-1};
        Node root =bt.createTree(array);
        ArrayList<Node> list = bt.postOrder(root);
        for(Node node : list) {
            System.out.print(node.value + " ");
        }

    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值