Morris遍历
- 一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1)(递归函数是系统压栈,代价:二叉树的高度)
- 通过利用原树中大量空闲指针的方式,达到节省空间的目的
上图Morris序:1,2,4,2,5,1,3,6,3,7
没有左树的节点只能到一次,有左树的节点能到两次,Morris遍历可以知道是第几次到达的当前节点(通过左树的最右节点的指针)
public void morris(Node head){
if(head==null){
return;
}
Node cur=head;
Node mostRight=null;
while(cur!=null){//情况三作为退出循环的条件
mostRight=cur.left;
if(mostRight!=null){//cur有左孩子
while(mostRight.right!=null&&mostRight.right!=cur){//别忘了后面这个判断条件
mostRight=mostRight.right;
}
//此时mostRight是cur左子树上的最右节点
if(mostRight.right==null){
mostRight.right=cur;
cur=cur.left;
continue;
}else{//mostRight.right=cur
mostRight.right=null;
}
}
cur=cur.right;
}
}
额外空间复杂度显然是O(1)
时间复杂度:
keypoint:所有节点遍历左子树右边界的总代价,所有节点遍历左子树的右边界不会重复,总代价O(N)
如何将Morris序改成先序?
如果一个节点只能到达一次,就直接打印(没有左子树)
如果一个节点能到达两次,第一次打印(有左子树)
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){//第一次来到cur
System.out.println(cur.value);
mostRight.right=cur;
cur=cur.left;
continue;
}else{
mostRight.right=null;
}
}else{//没有左子树的情况
System.out.println(cur.value);
}
cur=cur.right;
}
}
中序遍历:
如果一个节点只能到达一次,直接打印(没有左树)如果一个节点能到达两次,第二次来到的时候打印(有左树)
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.println(cur.value);//优化过
cur=cur.right;
}
}
后序遍历
当一个节点来到第二次的时候,逆序打印当前节点左树右边界
整个过程都进行完之后:单独逆序打印整棵树的右边界
如何做到逆序打印左树右边界,并且时间复杂度为O(N),空间复杂度为O(1)
单链表的逆序操作+复原
public 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;
}
//最后打印整颗d
printEdge(head);
}
public void printEdge(Node x){
Node tail=reverseEdge(x);
Node cur=tail;
while(cur!=null){
System.out.print(cur.value+" ");
cur=cur.right;
}
reverseEdge(tail);
}
public 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;
}