Morris遍历
一种遍历二叉树的方式,并且时间复杂度
O(N)
,额外空间复杂度
O(1)
通过利用原树中大量空闲指针的方式(一般是某节点的右叶子的右指针),达到节省空间的目的
Morris遍历细节
假设来到当前节点
cur
,开始时
cur
来到头节点位置
1
)如果
cur
没有左孩子,
cur
向右移动
(cur = cur.right)
2
)如果
cur
有左孩子,找到左子树上最右的节点
mostRight
:
a.如果mostRight的右指针指向空,让其指向
cur
, 然后cur
向左移动
(cur = cur.left)
b.如果mostRight
的右指针指向
cur
,让其指向
null
, 然后cur
向右移动
(cur = cur.right)
3
)
cur
为空时遍历停止
Morris遍历的实质
建立一种机制,对于没有左子树的节点只到达一次,对于有左子树的节点会到达两次
morris
遍历时间复杂度的证明
Morris前中后序遍历
先序、中序可以由
morris
遍历加工得到
先序:只经过一次的节点,直接打印,经过两次的节点,在第一次经过的时候打印。
// morris中序排序
public static void preMorris(Node node) {
// 定义cur指针
Node cur = node;
// 定义mostRight节点
Node cur2 = null;
// 如果cur指向的不为空则继续
while (cur != null) {
// 看此节点是否有左子树
cur2 = cur.left;
if (cur2 != null) {
// 如果有,找到mostRight节点,指向cur
while (cur2.right != null && cur2.right != cur) {
cur2 = cur2.right;
}
// 如果最右节点的右子树为空,cur第一次来到此节点
if (cur2.right == null) {
cur2.right = cur;
System.out.print(cur.value + " ");
cur = cur.left;
continue;
} else {
// cur第二次来到此节点
cur2.right = null;
}
} else {
System.out.print(cur.value + " ");
}
cur = cur.right;
}
System.out.println();
}
中序:只经过一次的节点,直接打印,经过两次的节点,在第二次经过的时候打印。
// morris中序排序
public static void inMorris(Node node) {
Node cur = node;
Node cur2 = null;
while (cur != null) {
cur2 = cur.left;
if (cur2 != null) {
while (cur2.right != null && cur2.right != cur) {
cur2 = cur2.right;
}
if (cur2.right != cur) {
cur2.right = cur;
cur = cur.left;
continue;
} else {
cur2.right = null;
}
}
System.out.print(cur.value + " ");
cur = cur.right;
}
System.out.println();
}
后序遍历也可由
morris
遍历加工得到,但是把处理时机放在,能够达到两次的节点并且是第二次
到达的时候,第二次经过节点时,逆序打印该节点左子树的最右边界。最后打印整棵树的右边界。
// morris后序排序
public static void posMorris(Node node) {
Node cur = node;
Node cur2 = null;
while (cur != null) {
cur2 = cur.left;
if (cur2 != null) {
if (cur2.right != null && cur2.right != cur) {
cur2 = cur2.right;
}
if (cur2.right != cur) {
cur2.right = cur;
cur = cur.left;
continue;
} else {
cur2.right = null;
printRightEdge(cur.left);
}
}
cur = cur.right;
}
printRightEdge(node);
}
// 逆序打印左子树的右边界
public static void printRightEdge(Node node) {
// 先逆序
Node pre = null;
while (node != null) {
Node next = node.right;
node.right = pre;
pre = node;
node = next;
}
node = pre;
// 打印
while (pre != null) {
System.out.print(pre.value + " ");
pre = pre.right;
}
// 再逆序回来
while (node != null) {
Node next = node.right;
node.right = pre;
pre = node;
node = next;
}
}
叉树节点间的最大距离问题
从二叉树的节点
a
出发,可以向上或者向下走,但沿途的节点只能经过一次,到达节点
b
时路
径上的节点个数叫作
a
到
b
的距离,那么二叉树任何两个节点之间都有距离,求整棵树上的最
大距离。
public static void main(String[] args) {
Node head1 = new Node(1);
head1.left = new Node(2);
head1.right = new Node(3);
head1.left.left = new Node(4);
head1.left.right = new Node(5);
head1.right.left = new Node(6);
head1.right.right = new Node(7);
head1.left.left.left = new Node(8);
head1.right.left.right = new Node(9);
System.out.println(maxDistance(head1));
Info maxDis = findMaxDis(head1);
System.out.println(maxDis.maxDis);
Node head2 = new Node(1);
head2.left = new Node(2);
head2.right = new Node(3);
head2.right.left = new Node(4);
head2.right.right = new Node(5);
head2.right.left.left = new Node(6);
head2.right.right.right = new Node(7);
head2.right.left.left.left = new Node(8);
head2.right.right.right.right = new Node(9);
System.out.println(maxDistance(head2));
maxDis = findMaxDis(head2);
System.out.println(maxDis.maxDis);
}
/**
* 寻找二叉树中的最大路径
* 1.经过x,寻找x左子树的最大深度,寻找x的右子树最大深度
* 2.不经过x,左子树的最大路径,右子树的最大路径
*/
public static class Info {
int maxDis;
int maxHeight;
public Info(int maxDis, int maxHeight) {
this.maxHeight = maxHeight;
this.maxDis = maxDis;
}
}
public static Info findMaxDis(Node node) {
if (node == null) {
return new Info(0, 0);
}
// 构造左子树和右子树的info
Info leftInfo = findMaxDis(node.left);
Info rightInfo = findMaxDis(node.right);
//构造该节点的Info
int maxHeight = Integer.max(leftInfo.maxHeight, rightInfo.maxHeight) + 1;
int maxDis = Integer.max(leftInfo.maxHeight + rightInfo.maxHeight + 1, Integer.max(leftInfo.maxDis, rightInfo.maxDis));
return new Info(maxDis,maxHeight);
}