Morris遍历:利用叶子节点的left , right 这些空指针,使得非叶子节点能够借助这些空指针遍历两次,没有使用额外的空间。前中后遍历都是在第一次 / 第二次 的遍历过程中做不同的处理。Morris遍历可以将非递归遍历中的空间复杂度降为O(1)。从而实现时间复杂度为O(N),而空间复杂度为O(1)的精妙算法。
遍历原则:记作当前节点为cur
- 如果cur无左孩子,cur向右移动(cur = cur.right)
- 如果cur有左孩子,找到cur左子树上最右的节点,记作mostRight
- 如果mostRight的right指针指向空,让其指向cur,cur向左移动(cur = cur.left)
- 如果mostRight的right指针指向cur,让其指向null(mostRight.right == null ,恢复树结构),cur向右移动(cur = cur.right)
Morris遍历实质:对于没有左子树的节点之遍历一次,有左子树的节点遍历两次
Morris遍历实现二叉树的前中后遍历
class TreeNode{
int val;
TreeNode left;
TreeNode right;
}
public class Morris {
public void morris(TreeNode head){
if(head == null){
return ;
}
TreeNode cur = head;
TreeNode mostRigth = null;
while(cur != null){
mostRigth = cur.left;
if(mostRigth != null){
//找cur.left 的最右节点
while(mostRigth.right != null && mostRigth.right != cur){
mostRigth = mostRigth.right;
}
//对mostRight 进行判断,进而确定是第几次来来到 cur 节点
if(mostRigth.right == null){
//第一次来到 cur,将 mostRight.right 指向cur,这也是第二次回到cur 的唯一方法
mostRigth.right = cur;
//cur 左移
cur = cur.left;
continue;
}else{
//第二次来到 cur 节点,将mostRight.right 指向null(恢复树结构)
mostRigth.right = null;
}
}
//cur.left == null的话,这就是第一次来到cur.left,也是唯一一次,cur右移
cur = cur.right;
}
}
//morrisPre
public void morrisPre(TreeNode head){
if(head == null){
return;
}
TreeNode cur = head;
TreeNode 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.println(cur.val);
//mostRight.right 指向 cur
mostRight.right = cur;
//左子树不为空,且左子树的最右节点为null,左移
cur = cur.left;
continue;
} else {
//第二次
mostRight.right = null;//恢复树
}
}else {
//cur.left等于空直接打印
System.out.println(cur.val);
}
//左子树为空,右移
cur = cur.right;
}
}
//morrisIn
//在第二次打印,没有左子树的节点只会遍历一次,直接打印
public List<Integer> morrisIn(TreeNode root){
List<Integer> list = new ArrayList<>();
if(root == null){
return list;
}
TreeNode cur = root;
TreeNode 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;//恢复树结构
}
}
list.add(cur.val);
cur = cur.right;
}
return list;
}
//morrisPost
/*
TODO: 第二次来到 cur 时,逆序打印 cur.left 为根节点的子树的右边界
退出while后再逆序打印 root 的右边界
*/
public void morrisPost(TreeNode root){
if(root == null){
return;
}
TreeNode cur = root;
TreeNode 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{
//恢复结构后,逆序打印cur.left的右边界
mostRight.right = null;
printEdge(cur.left);
}
}
cur = cur.right;
}
//逆序打印root的右边界
printEdge(root);
return;
}
public void printEdge(TreeNode node){
TreeNode tail = reverseNEdge(node);
TreeNode cur = tail;
while (cur != null){
System.out.println(cur.val);
cur = cur.right;
}
reverseNEdge(tail);
}
public TreeNode reverseNEdge(TreeNode node){
if(node == null){
return null;
}
TreeNode pre = null;
TreeNode next = null;
while(node != null){
next = node.right;
node.right = pre;
pre = node;
node = next;
}
return pre;
}
}