1. 程式人生 > >Morris遍歷詳解——二叉樹先序中序後序遍歷( 時間複雜度O(N),空間複雜度O(1) )

Morris遍歷詳解——二叉樹先序中序後序遍歷( 時間複雜度O(N),空間複雜度O(1) )

Morris二叉樹遍歷:

來到當前的節點:Cur
  1. 如果Cur無左孩子,Cur向右移動 (Cur = Cur.right)
  2. 如果Cur有左孩子,找到Cur左子樹上最右的節點,記為 mostright
  • (1) 如果mostright的右指標為null,則讓其右指標指向Cur,並使Cur向左移動 (Cur = Cur.left)
  • (2) 如果mostright的右指標指向Cur,則讓其右指標指向null,並使Cur向右移動 (Cur = Cur.right)
思路:(可模擬先序中序後序)
  1. 該節點有左子節點,則可以訪問到該節點兩次
  2. 該節點無左子節點,則只能訪問到該節點一次
#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;
}