Morris遍历

94 阅读2分钟

介绍

一种遍历二叉树的方式,并且时间复杂度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遍历时间复杂度依然是O(N

二叉树前中后序遍历

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;
   }
}

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();
}

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();
}

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为头的树中,最小深度是多少?

// 根据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;
}