您的位置:首页 > 其它

zz Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)

2015-04-28 03:08 471 查看
转载出处 http://www.cnblogs.com/AnnieKim/archive/2013/06/15/MorrisTraversal.html
博主很认真,这里转载一下以防丢失链接

本文主要解决一个问题,如何实现二叉树的前中后序遍历,有两个要求:

1. O(1)空间复杂度,即只能使用常数空间;

2. 二叉树的形状不能被破坏(中间过程允许改变其形状)。

通常,实现二叉树的前序(preorder)、中序(inorder)、后序(postorder)遍历有两个常用的方法:一是递归(recursive),二是使用栈实现的迭代版本(stack+iterative)。这两种方法都是O(n)的空间复杂度(递归本身占用stack空间或者用户自定义的stack),所以不满足要求。(用这两种方法实现的中序遍历实现可以参考这里。)

Morris Traversal方法可以做到这两点,与前两种方法的不同在于该方法只需要O(1)空间,而且同样可以在O(n)时间内完成。

要使用O(1)空间进行遍历,最大的难点在于,遍历到子节点的时候怎样重新返回到父节点(假设节点中没有指向父节点的p指针),由于不能用栈作为辅助空间。为了解决这个问题,Morris方法用到了线索二叉树(threaded binary tree)的概念。在Morris方法中不需要为每个节点额外分配指针指向其前驱(predecessor)和后继节点(successor),只需要利用叶子节点中的左右空指针指向某种顺序遍历下的前驱节点或后继节点就可以了。

Morris只提供了中序遍历的方法,在中序遍历的基础上稍加修改可以实现前序,而后续就要再费点心思了。所以先从中序开始介绍。

首先定义在这篇文章中使用的二叉树节点结构,即由val,left和right组成:

1 struct TreeNode {
2     int val;
3     TreeNode *left;
4     TreeNode *right;
5     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
6 };


一、中序遍历

步骤:

1. 如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。

2. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。

a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。

b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。当前节点更新为当前节点的右孩子。

3. 重复以上1、2直到当前节点为空。

图示:

下图为每一步迭代的结果(从左至右,从上到下),cur代表当前节点,深色节点表示该节点已输出。

package Tree;

/*
* Morris Traversal of Tree
* Threaded Binary Tree
* http://www.cnblogs.com/AnnieKim/archive/2013/06/15/morristraversal.html */

public class MorrisTrav {
private static class TreeNode {
int val;
TreeNode left, right;
public TreeNode (int val) {
this.val = val;
}
}
public void inorderMorrisTraversal(TreeNode root) {
TreeNode cur = root, pre = null;
while (cur != null) {
if (cur.left == null) {
System.out.print(cur.val+" ");
cur = cur.right;
} else {
pre = cur.left;
while (pre.right != null && pre.right != cur) {
pre = pre.right;
}
if (pre.right == null) {
pre.right = cur;
cur = cur.left;
} else { // pre.right == cur;
System.out.print(cur.val+" ");
pre.right = null;
cur = cur.right;
}
}
}
}
public void preorderMorrisTraversal(TreeNode root) {
TreeNode cur = root, pre = null;
while (cur != null) {
if (cur.left == null) {
System.out.print(cur.val+" ");
cur = cur.right;
} else {
pre = cur.left;
while (pre.right != null && pre.right != cur)
pre = pre.right;
if (pre.right == null) {
System.out.print(cur.val+" ");
pre.right = cur;
cur = cur.left;
} else {
pre.right = null;
cur = cur.right;
}
}
}
}
public void postorderMorrisTraversal(TreeNode root) {
TreeNode dump = new TreeNode(-1);
dump.left = root;
TreeNode cur = dump, pre = null;
while (cur != null) {
if (cur.left == null) {
cur = cur.right;
} else {
pre = cur.left;
while (pre.right != null && pre.right != cur) {
pre = pre.right;
}
if (pre.right == null) {
pre.right = cur;
cur = cur.left;
} else {
pre.right = null;
reversePrint(cur.left, pre);
cur = cur.right;
}
}
}
}
private void reversePrint(TreeNode from, TreeNode to) {
reverse(from, to);
TreeNode run = to;
while (true) {
System.out.print(run.val+" ");
if (run == from) break;
run = run.right;
}
reverse(to, from);
}
private void reverse(TreeNode from, TreeNode to) {
if (from == to) return;
TreeNode x = from, y = from.right, z = null;
while (true) {
z = y.right;
y.right = x;
x = y;
y = z;
if (x == to) {
break;
}
}
}
public static void main(String[] args) {
TreeNode n0 = new TreeNode(1);
TreeNode n1 = new TreeNode(2);
TreeNode n2 = new TreeNode(3);
TreeNode n3 = new TreeNode(4);
TreeNode n4 = new TreeNode(5);
TreeNode n5 = new TreeNode(6);
TreeNode n6 = new TreeNode(7);
TreeNode n7 = new TreeNode(8);
TreeNode n8 = new TreeNode(9);

n5.left = n1; n5.right = n6;
n1.left = n0; n1.right = n3;
n3.left = n2; n3.right = n4;
n6.right = n8;
n8.left = n7;

MorrisTrav inOrder = new MorrisTrav();
inOrder.inorderMorrisTraversal(n5);
System.out.println();
MorrisTrav preOrder = new MorrisTrav();
preOrder.preorderMorrisTraversal(n5);
System.out.println();
MorrisTrav postOrder = new MorrisTrav();
postOrder.postorderMorrisTraversal(n5);
}
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐