Morris遍歷詳解——二叉樹先序中序後序遍歷( 時間複雜度O(N),空間複雜度O(1) )
阿新 • • 發佈:2019-01-25
Morris二叉樹遍歷:
來到當前的節點:Cur
- 如果Cur無左孩子,Cur向右移動 (Cur = Cur.right)
- 如果Cur有左孩子,找到Cur左子樹上最右的節點,記為 mostright
- (1) 如果mostright的右指標為null,則讓其右指標指向Cur,並使Cur向左移動 (Cur = Cur.left)
- (2) 如果mostright的右指標指向Cur,則讓其右指標指向null,並使Cur向右移動 (Cur = Cur.right)
思路:(可模擬先序中序後序)
- 該節點有左子節點,則可以訪問到該節點兩次
- 該節點無左子節點,則只能訪問到該節點一次
#include <iostream>
using namespace std;
//樹的節點型別
class Node{
public:
int val;
Node *left, *right;
explicit Node(int x) :val(x), left(nullptr), right(nullptr) {}
};
void printEdge(Node*&);
void reverseEdge(Node *&);
Morris先序遍歷:
//Morris先序
void MorrisPre(Node* head)
{
if (head == nullptr)return ;
Node *cur = head;
Node *mostRight = nullptr;
while (cur != nullptr)
{
mostRight = cur->left;
//【1、2、】判斷Cur的左孩子是否為空
if (cur->left != nullptr)
{
//不斷向右尋找Cur左子樹最右的節點【mostRight右節點不為空 或者 不為當前cur節點,則非最右】
while (mostRight->right != nullptr && mostRight->right != cur)
{
mostRight = mostRight->right;
}
// { 隱含意思:為空表示第一次來到該節點位置,為cur表示第二次來到該節點位置 }
//【2、(1)、】若最右節點為空
if (mostRight->right == nullptr)//{ 第一次來到該節點, 將該節右指標設為cur,表示該節點下一步將移動到cur }
{
//////【---【先序遍歷】: 當前指標cur要準備往左孩子走了,馬上列印】
cout << "第一次:" << cur->val << endl;
mostRight->right = cur;
cur = cur->left;
continue;
}
else//【2、(2)、】若最右節點為當前節點cur
{// { 第二次來到該節點, 將該節點右指標設為原來的null,
// 前一步其實已經將當前cur指標指向了該節點的右節點了,即當前的cur已經是當前mostright的下一步節點了}
mostRight->right = nullptr;
}
}
else//當前cur沒有左孩子的時候
{
//////【---【先序遍歷】: 當前指標cur沒有左孩子,要準備往右孩子走了,馬上列印】
cout << "第二次:" << cur->val << endl;
}
//【1、】Cur的左孩子為空 或者【 2、(2)、】mostright的右孩子為Cur,即第二次訪問到該節點的時候
cur = cur->right;
}
}
Morris中序遍歷:
void MorrisIn(Node* head)
{
if (head == nullptr)return;
Node *cur = head;
Node *mostRight = nullptr;
while (cur != nullptr)
{
mostRight = cur->left;
//【1、2、】判斷Cur的左孩子是否為空
if(cur->left != nullptr)
{
//不斷向右尋找Cur左子樹最右的節點【mostRight右節點不為空 或者 不為當前cur節點,則非最右】
while (mostRight->right != nullptr && mostRight->right != cur)
{
mostRight = mostRight->right;
}
// { 隱含意思:為空表示第一次來到該節點位置,為cur表示第二次來到該節點位置 }
//【2、(1)、】若最右節點為空
if(mostRight->right == nullptr)//{ 第一次來到該節點, 將該節右指標設為cur,表示該節點下一步將移動到cur }
{
mostRight->right = cur;
cur = cur->left;
continue;
}else//【2、(2)、】若最右節點為當前節點cur
{// { 第二次來到該節點, 將該節點右指標設為原來的null,
// 前一步其實已經將當前cur指標指向了該節點的右節點了,即當前的cur已經是當前mostright的下一步節點了}
mostRight->right = nullptr;
}
}else//當前cur沒有左孩子的時候
{
}
//////【---【中序遍歷】: 當前指標cur要準備往右孩子走了,馬上列印】
cout << "第二次:" << cur->val << endl;
//【1、】Cur的左孩子為空 或者【 2、(2)、】mostright的右孩子為Cur,即第二次訪問到該節點的時候
cur = cur->right;
}
}
Morris後序遍歷:
void MorrisPos(Node* head)
{
if (head == nullptr)return;
Node *cur = head;
Node *mostRight = nullptr;
while (cur != nullptr)
{
mostRight = cur->left;
//【1、2、】判斷Cur的左孩子是否為空
if (cur->left != nullptr)
{
//不斷向右尋找Cur左子樹最右的節點【mostRight右節點不為空 或者 不為當前cur節點,則非最右】
while (mostRight->right != nullptr && mostRight->right != cur)
{
mostRight = mostRight->right;
}
// { 隱含意思:為空表示第一次來到該節點位置,為cur表示第二次來到該節點位置 }
//【2、(1)、】若最右節點為空
if (mostRight->right == nullptr)//{ 第一次來到該節點, 將該節右指標設為cur,表示該節點下一步將移動到cur }
{
mostRight->right = cur;
cur = cur->left;
continue;
}
else//【2、(2)、】若最右節點為當前節點cur
{// { 第二次來到該節點, 將該節點右指標設為原來的null,
// 前一步其實已經將當前cur指標指向了該節點的右節點了,即當前的cur已經是當前mostright的下一步節點了}
mostRight->right = nullptr;
//////【---【後序遍歷】: *逆序*列印當前節點cur的左子樹的右邊界】
printEdge(cur->left);
}
}
else//當前cur沒有左孩子的時候
{
}
//【1、】Cur的左孩子為空 或者【 2、(2)、】mostright的右孩子為Cur,即第二次訪問到該節點的時候
cur = cur->right;
}
//////【---【後序遍歷】: Morris遍歷完成後單獨*逆序*列印整棵樹的右邊界】
printEdge(head);
}
/*
* 逆序列印左子樹的右邊界
*/
void printEdge(Node *& node)
{
reverseEdge(node);
Node *lastNode = nullptr;
Node *nextNode = node->right;
while (nextNode != nullptr)
{
cout << node->val << endl;
node->right = lastNode;
lastNode = node;
node = nextNode;
nextNode = nextNode->right;
}
cout << node->val << endl;
node->right = lastNode;
}
/*
* 類似單鏈表反轉,O(1),將子樹最右邊界進行反轉
*/
void reverseEdge(Node *&node)
{
Node *lastNode = nullptr;
Node *nextNode = node->right;
while (nextNode != nullptr)
{
node->right = lastNode;
lastNode = node;
node = nextNode;
nextNode = nextNode->right;
}
node->right = lastNode;
}