二叉树的普通遍历方式(递归和迭代)时间复杂度为O(n),空间复杂度也为O(n),在空间上这种方式通过压栈实现,所以空间复杂度为O(n)。**Morris**遍历法可以在时间复杂度为O(n)的基础上,实现**O(1)**的空间复杂度(这是通过利用了二叉树中空闲的指针来实现的)。
Morris遍历通用逻辑
public static void morris(Node head) {if (null == head) {return ;}Node cur = head;//当前节点cur左子树的最右子节点Node mostRight = null;while (cur != null) {mostRight = cur.left;if (mostRight != null) {//找到cur左子树的最右子节点while (mostRight.right != null && mostRight.right != cur) {mostRight = mostRight.right;}//现在mostRight是左子树最右子节点if (mostRight.right == null) {//第一次来到cur//先将这个节点的右指针指向curmostRight.right = cur;//转移curcur = cur.left;continue;} else {//现在是第二次到达了cur节点,把第一次到达的时候设置的指针撤销mostRight.right = null;}}//没有左节点或第二次到达某个节点就转移到右节点cur = cur.right;}}
<br />`Morris`遍历的**前中后序**方案使用**一套通用代码 ,**主要的区别在于**在什么时机进行遍历操作**。
前序遍历
在每一个节点第一次遍历的时候进行打印操作。
public static void morrisPre(Node head) {if (null == head) {return ;}Node cur = head;//当前节点cur左子树的最右子节点Node mostRight = null;while (cur != null) {mostRight = cur.left;if (mostRight != null) {//找到cur左子树的最右子节点while (mostRight.right != null && mostRight.right != cur) {mostRight = mostRight.right;}//现在mostRight是左子树最右子节点if (mostRight.right == null) {//树枝节点System.out.print(cur.val+" ");//第一次来到cur//先将这个节点的右指针指向curmostRight.right = cur;//转移curcur = cur.left;continue;} else {//现在是第二次到达了cur节点,把第一次到达的时候设置的指针撤销mostRight.right = null;}} else {//叶子节点System.out.print(cur.val + " ");}//转移到右节点cur = cur.right;}}
中序遍历
- 只出现一次的节点直接打印
- 出现两次的节点,在第二次出现的时候打印
public static void morrisInOrder(Node head) {if (null == head) {return ;}Node cur = head;//当前节点cur左子树的最右子节点Node mostRight = null;while (cur != null) {mostRight = cur.left;if (mostRight != null) {//找到cur左子树的最右子节点while (mostRight.right != null && mostRight.right != cur) {mostRight = mostRight.right;}//现在mostRight是左子树最右子节点if (mostRight.right == null) {//第一次来到cur//先将这个节点的右指针指向curmostRight.right = cur;//转移curcur = cur.left;continue;} else {//现在是第二次到达了cur节点,把第一次到达的时候设置的指针撤销System.out.print(cur.val+" ");mostRight.right = null;}} else {//只出现一次System.out.print(cur.val+" ");}//转移到右节点cur = cur.right;}}
后序遍历
在可以出现两次的节点的第二次出现的时候,逆序打印其左子树的右边路径(通过反转链表手段)。public static void morrisPost(Node head) {if (null == head) {return ;}Node cur = head;//当前节点cur左子树的最右子节点Node mostRight = null;while (cur != null) {mostRight = cur.left;if (mostRight != null) {//找到cur左子树的最右子节点while (mostRight.right != null && mostRight.right != cur) {mostRight = mostRight.right;}//现在mostRight是左子树最右子节点if (mostRight.right == null) {//第一次来到cur//先将这个节点的右指针指向curmostRight.right = cur;//转移curcur = cur.left;continue;} else {//现在是第二次到达了cur节点,把第一次到达的时候设置的指针撤销mostRight.right = null;//逆序打印左子树的右边界,通过翻转链表实现reversePrint(cur.left);}}//转移到右节点cur = cur.right;}reversePrint(head);}public static void reversePrint(Node node) {Node head = reverse(node);Node current = head;while (current!=null) {System.out.print(current.val+" ");current = current.right;}reverse(head);}public static Node reverse(Node node) {Node head = node;Node pre = null;Node current = head;while (current != null) {Node right = current.right;current.right = pre;pre = current;current = right;}return pre;}
