Morris遍历介绍与应用
Morris遍历是一种二叉树遍历方法,通过改写指针来实现无额外空间复杂度的遍历。
Morris遍历适用于内存紧张的环境,如嵌入式编程和系统编程,以减少空间占用。
Morris遍历的核心在于利用节点上的空闲指针,通过巧妙地改变指针指向来实现遍历。
时间复杂度为O(n),空间复杂度为O(1)
Morris实现代码
public static 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.right == null) {
mostRight.right = cur;
cur = cur.left;
continue;
} else {
mostRight.right = null;
}
}
cur = cur.right;
}
}
前序遍历
- 从根节点开始,如果当前节点未被访问,检查其是否有左子节点。
- 如果当前节点没有左子节点,访问当前节点,然后转向其右子节点。
- 如果当前节点有左子节点,找到左子树中的最右节点(即左子树的“前序遍历的最后一个节点”),这个节点称为“前驱节点”。
- 如果前驱节点的右子节点为空,将当前节点链接到前驱节点的右子节点上,并将当前节点标记为已访问,然后转向当前节点的左子节点。
- 如果前驱节点的右子节点是当前节点,说明当前节点已经被访问过,那么断开这个链接,访问当前节点,然后转向当前节点的右子节点。
- 重复步骤1-5,直到所有节点都被访问。
public static 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) {
System.out.print(cur.value + " ");
mostRight.right = cur;
cur = cur.left;
continue;
} else {
mostRight.right = null;
}
} else {
System.out.print(cur.value + " ");
}
cur = cur.right;
}
System.out.println();
}
中序遍历
- 从根节点开始,如果当前节点未被访问,检查其是否有左子节点。
- 如果当前节点没有左子节点,访问当前节点,然后转向其右子树。
- 如果当前节点有左子节点,找到左子树中的最右节点(即左子树中最后一个被中序遍历的节点),这个节点称为“前驱节点”。
- 如果前驱节点的右子节点为空,将当前节点链接到前驱节点的右子节点上,并将当前节点标记为已访问,然后转向当前节点的左子树。
- 如果前驱节点的右子节点是当前节点,说明当前节点已经被访问过,那么断开这个链接,访问当前节点,然后转向当前节点的右子树。
- 重复步骤1-5,直到所有节点都被访问。
public static 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;
}
System.out.println();
}
后序遍历
- 首先检查头节点
head
是否为空,如果为空则直接返回。 - 使用一个
cur
指针遍历树,同时使用mostRight
指针找到cur
左子树中的最右节点。 - 如果
mostRight
的右指针为空,将其指向cur
,然后移动cur
到其左子树。 - 如果
mostRight
的右指针已经指向cur
,则说明已经访问过cur
的左子树,此时将mostRight
的右指针设为null
,打印从cur
左子树到cur
的边,然后继续遍历到cur
的右子树。 - 最后,打印从
head
到树的最右节点的边。
public static void morrisPos(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;
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 = null;
while (from != null) {
next = from.right;
from.right = pre;
pre = from;
from = next;
}
return pre;
}
给定一棵二叉树的头结点head,求以head为头的树中,最小深度是多少?
递归法
public static class Node {
public int val;
public Node left;
public Node right;
public Node(int x) {
val = x;
}
}
public static int minHeight1(Node head) {
if (head == null) {
return 0;
}
return p(head);
}
// 返回x为头的树,最小深度是多少
public static int p(Node x) {
if (x.left == null && x.right == null) {
return 1;
}
// 左右子树起码有一个不为空
int leftH = Integer.MAX_VALUE;
if (x.left != null) {
leftH = p(x.left);
}
int rightH = Integer.MAX_VALUE;
if (x.right != null) {
rightH = p(x.right);
}
return 1 + Math.min(leftH, rightH);
}
Morris法
// 根据morris遍历改写
public static int minHeight2(Node head) {
if (head == null) {
return 0;
}
Node cur = head;
Node mostRight = null;
int curLevel = 0;
int minHeight = Integer.MAX_VALUE;
while (cur != null) {
mostRight = cur.left;
if (mostRight != null) {
int rightBoardSize = 1;
while (mostRight.right != null && mostRight.right != cur) {
rightBoardSize++;
mostRight = mostRight.right;
}
if (mostRight.right == null) { // 第一次到达
curLevel++;
mostRight.right = cur;
cur = cur.left;
continue;
} else { // 第二次到达
if (mostRight.left == null) {
minHeight = Math.min(minHeight, curLevel);
}
curLevel -= rightBoardSize;
mostRight.right = null;
}
} else { // 只有一次到达
curLevel++;
}
cur = cur.right;
}
int finalRight = 1;
cur = head;
while (cur.right != null) {
finalRight++;
cur = cur.right;
}
if (cur.left == null && cur.right == null) {
minHeight = Math.min(minHeight, finalRight);
}
return minHeight;
}