二叉树遍历的非递归实现方法

二叉树的遍历,通常分为前序、中序、后序和层序遍历。而前序、中序和后序遍历,我们一般刚开始接触的都是递归实现的方式,这种情况不再赘述,本文是主要介绍的是二叉树遍历的非递归实现方法。

首先,定义树节点的结构:

class TreeNode {
	int val;
	TreeNode left;
	TreeNode right;
	TreeNode(int x) { 
		val = x; 
	}
}

1.堆栈实现


递归说白了就是栈的调用,这里我们使用Stack来自己实现,用堆栈实现的空间复杂度为O(n)。

前序遍历:

public void preorderStack(TreeNode root) {
    if (root == null) {
        return ;
    }
    Stack<TreeNode> stack = new Stack<TreeNode> ();
    stack.push(root);
    while (!stack.isEmpty()) {
        TreeNode node = stack.pop();
        System.out.println(node.val);
        if (node.right != null) {
            stack.push(node.right);
        }
        if (node.left != null) {
            stack.push(node.left);
        }
    }
}
该方法每次处理一个root节点时,首先输出root的值,然后先将root的右子树入栈,再将root的左子树入栈,如果栈不为空,将栈顶元素赋值给root,循环该过程。

中序遍历:

public void inorderStack(TreeNode root) {
    if (root == null) {
        return;
    }
    Stack<TreeNode> stack = new Stack<TreeNode> ();
    TreeNode cur = root;
    while (!stack.isEmpty() || cur != null) {
        if (cur != null) {
            while (cur != null) {
            stack.push(cur);
            cur = cur.left;
            }
        } else {
            cur = stack.pop();
            System.out.println(cur.val);
            cur = cur.right;
        }
    }
}
该方法利用栈先进后出的特点,对于当前cur节点,依次将cur和cur的左子树入栈,这样会首先访问cur的左子树,然后访问cur。当cur为空的时候,我们取出栈顶的元素并输出该节点的值,然后将该节点的右节点赋值给cur,开始访问该节点的右子树。

后序遍历:

public void postorderStack(TreeNode root) {
    if (root == null) {
        return ;
    }
    Stack<TreeNode> stack = new Stack<TreeNode> ();
    stack.push(root);
    TreeNode pre = root;
    while (!stack.isEmpty()) {
        TreeNode cur = stack.peek();
        if ((cur.left == null && cur.right == null) || cur.left == pre || cur.right == pre) {
            System.out.println(stack.pop().val);
            pre = cur;
        } else {
            if (cur.right != null) {
                stack.push(cur.right);
            }
            if (cur.left != null) {
                stack.push(cur.left);
            } 
        }
    }
}

该方法利用pre来记录上一个访问的节点,如果当前节点cur为叶子节点,或者pre为cur的孩子节点,表明cur的值可以被输出,否则我们会把cur的右孩子和左孩子相继入栈。

2.Morris实现

Morris方法所使用的空间复杂度为O(1),对上述堆栈方法的O(n)空间复杂度有很大的改善,这里有一篇文章(http://www.cnblogs.com/AnnieKim/archive/2013/06/15/MorrisTraversal.html),对Moriis遍历讲得非常详细。这里我主要展示一下我的代码,并介绍一下我的Morris后序遍历方法。

前序遍历:

public void preorderMorris(TreeNode root) {
    if (root == null) {
        return;
    }
    TreeNode cur = root;
    while (cur != null) {
        if (cur.left != null) {
            TreeNode tmp = cur.left;
            while (tmp.right != null && tmp.right != cur) {
                tmp = tmp.right;
            }
            if (tmp.right == null) {
                System.out.println(cur.val);
                tmp.right = cur;
                cur = cur.left;
            } else {
                tmp.right = null;
                cur = cur.right;
            }
        } else {
            System.out.println(cur.val);
            cur = cur.right;
        }
    }
}

中序遍历:

public void inorderMorris(TreeNode root) {
    if (root == null) {
        return;
    }
    TreeNode cur = root;
    while (cur != null) {
        if (cur.left != null) {
            TreeNode tmp = cur.left;
            while (tmp.right != null && tmp.right != cur) {
                tmp = tmp.right;
            }
            if (tmp.right == null) {
                tmp.right = cur;
                cur = cur.left;
            } else {
                tmp.right = null;
                System.out.println(cur.val);
                cur = cur.right;
            }
        } else {
        	System.out.println(cur.val);
            cur = cur.right;
        }
    }
}
后序遍历:

public List<Integer> postorderMorris(TreeNode root) {
	List<Integer> list = new ArrayList<Integer> ();
    if (root == null) {
        return list;
    }
    TreeNode cur = root;
    while (cur != null) {
        if (cur.right != null) {
            TreeNode tmp = cur.right;
            while (tmp.left != null && tmp.left != cur) {
                tmp = tmp.left;
            }
            if (tmp.left == null) {
            	list.add(cur.val);
                tmp.left = cur;
                cur = cur.right;
            } else {
                tmp.left = null;
                cur = cur.left;
            }
        } else {
            list.add(cur.val);
            cur = cur.left;
        }
    }
    Collections.reverse(list);
    return list;
}

在引用的那篇文章的Morris后序遍历中,需要用到倒序输出的方法,这样无疑加大了代码量。这里,当最后遍历结果不是简单输出,而是保存在一个数组中时,我们可以利用先序遍历的思想,来得到后序遍历。由于先序遍历的访问顺序是:父节点-左孩子-右孩子,而后序遍历是:左孩子-右孩子-父节点,因此我们可以按照父节点-右孩子-左孩子的访问顺序来得到遍历的数组,然后将整个数组翻转,这样就能得到后序遍历的结果。其实这种思想不单单适用于Morris遍历,还适用于堆栈实现的遍历。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值