目錄
Morris遍歷 ,一種遍歷二叉樹的方式
時間複雜度O(N),額外空間複雜度O(1)
1. 牛逼的地方
普通遍歷,因爲本身樹沒有往回指的指針,所以需要遞歸用遞歸棧或者自己壓棧的方式,遍歷樹結構,額外空間複雜度爲O(h),h爲二叉樹的高度。
這裏通過利用原樹中大量空閒指針的方式,達到節省空間的目的。
2. 實質
建立一種機制,對於沒有左子樹的節點只到達一次,對於有左子樹的節點會到達兩次
3. 具體細節:
當前節點,最右節點,上一節點,下一節點,想到這些的時候記得重新弄個變量!!
Morris遍歷細節 假設來到當前節點cur,開始時cur來到頭節點位置
1)如果cur沒有左孩子,cur向右移動(cur = cur.right)
2)如果cur有左孩子,找到左子樹上最右的(爲空或者指向自己終止)節點mostRight:
while (mostRight.right != null && mostRight.right != cur) {
mostRight = mostRight.right;
}
a.如果mostRight的右指針指向空,讓其指向cur, 然後cur向左移動(cur = cur.left)
b.如果mostRight的右指針指向cur,讓其指向null, 然後cur向右移動(cur = cur.right)
3)cur爲空時遍歷停止
4. morris遍歷時間複雜度的證明
可以看出主要是找到左子樹上最右的節點,這裏涉及一個while循環,其他的肯定是O(N)的。
而且很容易判別這裏的while最多也就是趨近於O(N),所以總體還是O(N)的算法。
看下圖,所有左子樹的右邊界上的點都遍歷兩次,一次是null,然後指向自己,再左移,一次是指向自己,然後null,再右移。
5. 先序,中序,後序
moris序+先序+中序 示意圖
先序、中序可以由morris遍歷加工得到。
先序:能回到自己的兩次的節點,第一次打印,回到自己一次的直接打印。
中序:能回到自己的兩次的節點,第二次打印,回到自己一次的直接打印。
後序:後序遍歷也可由morris遍歷加工得到,但是把處理時機放在,能夠達到兩次的節點並且是第二次到達的時候。
具體就是第二次到的時候,打印左子樹的右邊界,最後記得打印整顆樹的右邊界。
6. 代碼
C++版本
// Moris遍歷
struct TreeNode
{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x):val(x),left(NULL),right(NULL){}// 這裏沒有分號
};
// 有左孩子的到達了兩次!!,沒有左孩子的到達一次
void Moris(TreeNode* root){// 第一次出現的時候打印
if(root==NULL) return root;
TreeNode* cur=root;// 當前節點
//TreeNode* left=cur->left;
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 = MostRight;// 如果爲空就指向自己
cur = cur->left;// 當前節點左移動
}
else// 第二次出現
{
MostRight->right = NULL;// 如果不爲空,就指向空
cur = cur->right;// 當前節點右移動
}
}
else//沒有左孩子的到達一次
{
cur=cur->right;//如果沒有左節點,當前節點右移動
}
}
}
void MorisPre(TreeNode* root){// 第一次出現的時候打印
if(root==NULL) return;
TreeNode* cur=root;// 當前節點
//TreeNode* left=cur->left;
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 = MostRight;// 如果爲空就指向自己
cout << cur->val<<" ";
cur = cur->left;// 當前節點左移動
}
else// 第二次出現
{
MostRight->right = NULL;// 如果不爲空,就指向空
cur = cur->right;// 當前節點右移動
}
}
else//沒有左孩子的到達一次
{
cout << cur->val<<" ";
cur=cur->right;//如果沒有左節點,當前節點右移動
}
}
}
void MorisIn(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 = MostRight;// 如果爲空就指向自己
cur = cur->left;// 當前節點左移動
}
else// 第二次出現
{
MostRight->right = NULL;// 如果不爲空,就指向空
cout << cur->val<<" ";
cur = cur->right;// 當前節點右移動
}
}
else
{
cout << cur->val<<" ";
cur=cur->right;//如果沒有左節點,當前節點右移動
}
}
}
void MorisPos(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!=left)
{
MostRight = MostRight->right;
}
if(MostRight->right==NULL){// 第一次出現
MostRight->right = MostRight;// 如果爲空就指向自己
cur = cur->left;// 當前節點左移動
}
else// 第二次出現
{
MostRight->right = NULL;// 如果不爲空,就指向空
printReverse(cur->left);// 逆序打印該節點左孩子的右邊界
cur = cur->right;// 當前節點右移動
}
}
else
{
cur=cur->right;//如果沒有左節點,當前節點右移動
}
}
printReverse(root);// 打印根節點對應的最右邊界
}
void printReverse(TreeNode* root)// 逆序打印該節點左孩子的右邊界
{
// 能第二次到的,說明肯定有左節點,因此不需要判斷左節點是否爲空
TreeNode* tail = ReverseEdge(root);// 逆序他的右邊界
TreeNode* cur =tail;
while(cur->right!=nullptr)
{
cout << cur->val<<" ";
cur=cur->right;//來到左子樹右邊界
}
ReverseEdge(tail);// 打印完後還原
TreeNode* ReverseEdge(TreeNode* root)
{
TreeNode* pre=NULL;
TreeNode* next=NULL;
TreeNode* cur =root;
while(cur!=NULL){
next = cur->right;
cur->right = pre;
pre = cur;
cur = next;
}
return pre;
}
package class05;
public class Code01_MorrisTraversal {
public static class Node {
public int value;
Node left;
Node right;
public Node(int data) {
this.value = data;
}
}
public static void process(Node head) {
if(head == null) {
return;
}
// 1
process(head.left);
// 2
process(head.right);
// 3
}
public static void morris(Node head) {
if (head == null) {
return;
}
Node cur = head;
Node mostRight = null;
while (cur != null) {
mostRight = cur.left; // mostRight是cur左孩子
if (mostRight != null) {
while (mostRight.right != null && mostRight.right != cur) {
mostRight = mostRight.right;
}
// mostRight變成了cur左子樹上,最右的節點
if (mostRight.right == null) { // 這是第一次來到cur
mostRight.right = cur;
cur = cur.left;
continue;
} else { // 這是第二次來到cur
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; // mostRight是cur左孩子
if (mostRight != null) {
while (mostRight.right != null && mostRight.right != cur) {
mostRight = mostRight.right;
}
// mostRight變成了cur左子樹上,最右的節點
if (mostRight.right == null) { // 這是第一次來到cur
System.out.println(cur.value);
mostRight.right = cur;
cur = cur.left;
continue;
} else { // 這是第二次來到cur
mostRight.right = null;
}
} else { // 當前cur,只能來到一次
System.out.println(cur.value);
}
cur = cur.right;
}
}
public static void morrisIn(Node head) {
if (head == null) {
return;
}
Node cur = head;
Node mostRight = null;
while (cur != null) {
mostRight = cur.left; // mostRight是cur左孩子
if (mostRight != null) {
while (mostRight.right != null && mostRight.right != cur) {
mostRight = mostRight.right;
}
// mostRight變成了cur左子樹上,最右的節點
if (mostRight.right == null) { // 這是第一次來到cur
mostRight.right = cur;
cur = cur.left;
continue;
} else { // 這是第二次來到cur
mostRight.right = null;
}
}
System.out.println(cur.value);
cur = cur.right;
}
}
public static void morrisPos(Node head) {
if (head == null) {
return;
}
Node cur1 = head;
Node cur2 = null;
while (cur1 != null) {
cur2 = cur1.left;
if (cur2 != null) {
while (cur2.right != null && cur2.right != cur1) {
cur2 = cur2.right;
}
if (cur2.right == null) {
cur2.right = cur1;
cur1 = cur1.left;
continue;
} else {
cur2.right = null;
printEdge(cur1.left);
}
}
cur1 = cur1.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;
}
// for test -- print tree
public static void printTree(Node head) {
System.out.println("Binary Tree:");
printInOrder(head, 0, "H", 17);
System.out.println();
}
public static void printInOrder(Node head, int height, String to, int len) {
if (head == null) {
return;
}
printInOrder(head.right, height + 1, "v", len);
String val = to + head.value + to;
int lenM = val.length();
int lenL = (len - lenM) / 2;
int lenR = len - lenM - lenL;
val = getSpace(lenL) + val + getSpace(lenR);
System.out.println(getSpace(height * len) + val);
printInOrder(head.left, height + 1, "^", len);
}
public static String getSpace(int num) {
String space = " ";
StringBuffer buf = new StringBuffer("");
for (int i = 0; i < num; i++) {
buf.append(space);
}
return buf.toString();
}
public static void main(String[] args) {
Node head = new Node(4);
head.left = new Node(2);
head.right = new Node(6);
head.left.left = new Node(1);
head.left.right = new Node(3);
head.right.left = new Node(5);
head.right.right = new Node(7);
printTree(head);
morrisIn(head);
morrisPre(head);
morrisPos(head);
printTree(head);
}
}