题目
给定一棵二叉树的头节点 head,完成二叉树的先序、中序和后序遍历。如果二叉树的节点数为 N,则要求时间复杂度为 O(N),额外空间复杂度为 O(1)。
解答
要想使得遍历二叉树的额外空间复杂度为 O(1),那么就需要使用 Morris 遍历方法。
Morris 方法的思路,讲得最好的我认为是这一篇博客:https://www.cnblogs.com/anniekim/archive/2013/06/15/morristraversal.html
其中,最核心的是它讲述的步骤,Morris 遍历的原始版本其实是中序遍历,步骤如下
如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。
如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点(predecessor)。找到前驱节点之后,关于前驱节点,又要分为以下两种情况来处理:
重复以上 1、2 直到当前节点为空。
下面就是 Java 代码的全部实现
原始不打印节点的版本
Node
数据结构
1 2 3 4 5 6 7 8 9
| public class Node { public int value; public Node left; public Node right;
public Node(int value) { this.value = value; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public void morris(Node head) { if (head == null) { return; } Node cur = head; Node mostRight = null; while (cur != null) { mostRight = cur.left; if (mostRight != null) { while (mostRight.right != null && mostRight.right != cur) { mostRight = mostRight.right; } if (mostRight == null) { mostRight.right = cur; cur = cur.left; continue; } else { mostRight.right = null; } } cur = cur.right; } }
|
前序遍历版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public void morrisPre(Node head) { if (head == null) { return; } Node cur = head; Node mostRight = null; while (cur != null) { mostRight = cur.left; if (mostRight != null) { while (mostRight.right != null && mostRight.right != cur) { mostRight = mostRight.right; } if (mostRight.right == null) { mostRight.right = cur; System.out.print(cur.value + " "); cur = cur.left; continue; } else { mostRight.right = null; } } else { System.out.print(cur.value + " "); } cur = cur.right; } }
|
中序遍历版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public void morrisIn(Node head) { if (head == null) { return; } Node cur = head; Node mostRight = null; while (cur != null) { mostRight = cur.left; if (mostRight != null) { while (mostRight.right != null && mostRight.right != cur) { mostRight = mostRight.right; } if (mostRight.right == null) { mostRight.right = cur; cur = cur.left; continue; } else { mostRight.right = null; } } System.out.print(cur.value + " "); cur = cur.right; } }
|
后序遍历版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public void morrisPost(Node head) { if (head == null) { return; } Node cur = head; Node mostRight = null; while (cur != null) { mostRight = cur.left; if (mostRight != null) { while (mostRight.right != null && mostRight.ri mostRight = mostRight.right; } if (mostRight.right == null) { mostRight.right = cur; cur = cur.left; continue; } else { mostRight.right = null; printEdge(cur.left); } } cur = cur.right; } printEdge(head); System.out.println(); }
public static void printEdge(Node head) { Node tail = reverseEdge(head); Node cur = tail; while (cur != null) { System.out.print(cur.value + " "); cur = cur.right; } reverseEdge(tail); }
public static Node reverseEdge(Node from) { Node pre = null; Node next; while (from != null) { next = from.right; from.right = pre; pre = from; from = next; } return pre; }
|
后序版本稍微有点复杂,主要是多了一个逆转左子树的右边界的做法。
测试 main
方法
测试所用的二叉树

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public static void main(String[] args) { Node head = new Node(6); head.left = new Node(2); head.left.left = new Node(1); head.left.right = new Node(4); head.left.right.left = new Node(3); head.left.right.right = new Node(5); head.right = new Node(7); head.right.right = new Node(9); head.right.right.left = new Node(8); Moris_5 ms = new Moris_5(); ms.morrisPre(head); System.out.println("\n----------"); ms.morrisIn(head); System.out.println("\n----------"); ms.morrisPost(head); }
|
输出
1 2 3 4 5
| 6 2 1 4 3 5 7 9 8 ---------- 1 2 3 4 5 6 7 8 9 ---------- 1 3 5 4 2 8 9 7 6
|