左神进阶班 -morris 遍历: 带你装* 带你飞

基本模板:

3条规则:

1.如果 当前节点没有左子树 ,直接去右节点。

2.如果当前节点有左子树:则找到该左子树的最右节点:  如果最右节点的右指针为nullptr,则改为指向当前节点,

如果最右节点的右指针指向当前节点,则改为指向nullptr。

//基本框架
//morris 遍历的核心 在于 怎么从 递归 时打印的位置 去理解 先序 中序 后序的打印时机,从而明白  morris是如何做到 这样的遍历的
void morris(TreeNode* head){
	TreeNode* cur = head;
	TreeNode* mostRight = nullptr;
	while(cur != nullptr){
		mostRight = cur->left;
		//注意复用时删除这一条
		cout << cur->value <<endl;
		//如果左指针为空 直接访问右节点
		if(mostRight != nullptr){
			//先 找到左子树的最右边节点
			while(mostRight->right != nullptr && mostRight->right != cur){
				mostRight = mostRight->right;
			}
			//然后看其有指针是否为空,为空的话,则表示cur第一次到达当前节点,否则为第二次
			if(mostRight->right == nullptr){
				mostRight->right = cur;
				cur = cur->left;
				continue;
			}else{
				mostRight->right = nullptr;
			}
		}
		cur = cur->right;
	}
}

先序 :

//打印时机在第一次访问的时候,但该注意没有左子树的情况
void morrisPre(TreeNode* head){
	TreeNode* cur = head;
	TreeNode* mostRight = nullptr;
	while(cur != nullptr){
		mostRight = cur->left;
		//如果左指针为空 直接访问右节点,若不为空,则会两次访问当前节点
		if(mostRight != nullptr){
			//先 找到左子树的最右边节点
			while(mostRight->right != nullptr && mostRight->right != cur){
				mostRight = mostRight->right;
			}
			//然后看其有指针是否为空,为空的话,则表示cur第一次到达当前节点,否则为第二次
			if(mostRight->right == nullptr){
				//当有左子树的情况下,会访问到当前节点两次,则选第一次到达时打印 作为 先序遍历
				cout << cur->value <<endl;
				mostRight->right = cur;
				cur = cur->left;
				continue;
			}else{
				mostRight->right = nullptr;
			}
		}else{
			//但 有时候当前节点是没有左孩子的,所以直接打印当前节点,然后去到右子树
			cout << cur->value<<endl;
		}
		cur = cur->right;
	}
}

中序:

void morrisIn(TreeNode* head){
	TreeNode* cur = head;
	TreeNode* mostRight = nullptr;
	while(cur != nullptr){
		mostRight = cur->left;
		//如果左指针为空 直接访问右节点
		if(mostRight != nullptr){
			//先 找到左子树的最右边节点
			while(mostRight->right != nullptr && mostRight->right != cur){
				mostRight = mostRight->right;
			}
			//然后看其有指针是否为空,为空的话,则表示cur第一次到达当前节点,否则为第二次
			if(mostRight->right == nullptr){
				mostRight->right = cur;
				cur = cur->left;
				continue;
			}else{
				mostRight->right = nullptr;
			}
		}
		//当前语句执行的时机有两种: 第一种,没有左孩子,第二种,当前节点的左子树访问结束,访问当前节点,然后去右边
		//所以 中序的序列 ,在这输出
		cout << cur->value <<endl;
		cur = cur->right;
	}
}

 

后序:

//太秀了:morris 后序遍历的 做法:对于那些能第二次到达的节点,逆序遍历其节点的左子树 的最右边界。  然后 最后补上整棵树的最右边界逆序。
void morrisPost(TreeNode* head){
	TreeNode* cur = head;
	TreeNode* mostRight = nullptr;
	while(cur != nullptr){
		mostRight = cur->left;
		//如果左指针为空 直接访问右节点
		if(mostRight != nullptr){
			//先 找到左子树的最右边节点
			while(mostRight->right != nullptr && mostRight->right != cur){
				mostRight = mostRight->right;
			}
			//然后看其有指针是否为空,为空的话,则表示cur第一次到达当前节点,否则为第二次
			if(mostRight->right == nullptr){
				mostRight->right = cur;
				cur = cur->left;
				continue;
			}else{
				mostRight->right = nullptr;
				printEdge(cur->left);
			}
		}
		cur = cur->right;
	}
	printEdge(head);
}

后序的逆序遍历函数:

void printEdge(TreeNode* head){
	if(head->right == nullptr){
		cout << head->value<<endl;
		return ;
	}
	TreeNode* before = head;
	TreeNode* go = head->right;
	TreeNode* next = go->right;
	before->right = nullptr;
	while(go != nullptr){
		go->right = before;
		before = go;
		go = next;
		if(next != nullptr)
			next = next->right;
	}

	// 当 停止的时候,before 为最后一个节点
	go = before;
	while(go != nullptr){
		cout << go->value << endl;
		go = go->right;
	}
	// 把 指针重新调整回去
	go = before->right;
	next = go->right;
	before->right = nullptr;
	while(go != nullptr){
		go->right = before;
		before = go;
		go = next;
		if(next!= nullptr){
			next = next->right;
		}
	}
	//验证链表正确 调整
	// while(before != nullptr){
	// 	cout << before->value <<endl;
	// 	before = before->right;
	// }
}

整体:

 

#include<stack>
#include<iostream>
using namespace std;
struct TreeNode{
	int value;
	TreeNode* left;
	TreeNode* right;
	void setValue(int value){
		this->value = value;
		this->left = nullptr;
		this->right = nullptr;
	}
};
TreeNode* getTree(int arr[],int length,int i){
	if(i < length){
		TreeNode* node = new TreeNode();
		node->value = arr[i];
		if((2*i+1) < length){
			node->left = getTree(arr, length, 2*i+1);
		}else{
			node->left = nullptr;
		}
		if((2*i+2)<length){
			node->right = getTree(arr, length,2*i+2);
		}else{
			node->right = nullptr;
		}
		return node;
	}
	return nullptr;
}
//基本框架
//morris 遍历的核心 在于 怎么从 递归 时打印的位置 去理解 先序 中序 后序的打印时机,从而明白  morris是如何做到 这样的遍历的
void morris(TreeNode* head){
	TreeNode* cur = head;
	TreeNode* mostRight = nullptr;
	while(cur != nullptr){
		mostRight = cur->left;
		//注意复用时删除这一条
		cout << cur->value <<endl;
		//如果左指针为空 直接访问右节点
		if(mostRight != nullptr){
			//先 找到左子树的最右边节点
			while(mostRight->right != nullptr && mostRight->right != cur){
				mostRight = mostRight->right;
			}
			//然后看其有指针是否为空,为空的话,则表示cur第一次到达当前节点,否则为第二次
			if(mostRight->right == nullptr){
				mostRight->right = cur;
				cur = cur->left;
				continue;
			}else{
				mostRight->right = nullptr;
			}
		}
		cur = cur->right;
	}
}
//打印时机在第一次访问的时候,但该注意没有左子树的情况
void morrisPre(TreeNode* head){
	TreeNode* cur = head;
	TreeNode* mostRight = nullptr;
	while(cur != nullptr){
		mostRight = cur->left;
		//如果左指针为空 直接访问右节点,若不为空,则会两次访问当前节点
		if(mostRight != nullptr){
			//先 找到左子树的最右边节点
			while(mostRight->right != nullptr && mostRight->right != cur){
				mostRight = mostRight->right;
			}
			//然后看其有指针是否为空,为空的话,则表示cur第一次到达当前节点,否则为第二次
			if(mostRight->right == nullptr){
				//当有左子树的情况下,会访问到当前节点两次,则选第一次到达时打印 作为 先序遍历
				cout << cur->value <<endl;
				mostRight->right = cur;
				cur = cur->left;
				continue;
			}else{
				mostRight->right = nullptr;
			}
		}else{
			//但 有时候当前节点是没有左孩子的,所以直接打印当前节点,然后去到右子树
			cout << cur->value<<endl;
		}
		cur = cur->right;
	}
}

void morrisIn(TreeNode* head){
	TreeNode* cur = head;
	TreeNode* mostRight = nullptr;
	while(cur != nullptr){
		mostRight = cur->left;
		//如果左指针为空 直接访问右节点
		if(mostRight != nullptr){
			//先 找到左子树的最右边节点
			while(mostRight->right != nullptr && mostRight->right != cur){
				mostRight = mostRight->right;
			}
			//然后看其有指针是否为空,为空的话,则表示cur第一次到达当前节点,否则为第二次
			if(mostRight->right == nullptr){
				mostRight->right = cur;
				cur = cur->left;
				continue;
			}else{
				mostRight->right = nullptr;
			}
		}
		//当前语句执行的时机有两种: 第一种,没有左孩子,第二种,当前节点的左子树访问结束,访问当前节点,然后去右边
		//所以 中序的序列 ,在这输出
		cout << cur->value <<endl;
		cur = cur->right;
	}
}
void printEdge(TreeNode* head){
	if(head->right == nullptr){
		cout << head->value<<endl;
		return ;
	}
	TreeNode* before = head;
	TreeNode* go = head->right;
	TreeNode* next = go->right;
	before->right = nullptr;
	while(go != nullptr){
		go->right = before;
		before = go;
		go = next;
		if(next != nullptr)
			next = next->right;
	}

	// 当 停止的时候,before 为最后一个节点
	go = before;
	while(go != nullptr){
		cout << go->value << endl;
		go = go->right;
	}
	// 把 指针重新调整回去
	go = before->right;
	next = go->right;
	before->right = nullptr;
	while(go != nullptr){
		go->right = before;
		before = go;
		go = next;
		if(next!= nullptr){
			next = next->right;
		}
	}
	//验证链表正确 调整
	// while(before != nullptr){
	// 	cout << before->value <<endl;
	// 	before = before->right;
	// }
}
//太秀了:morris 后序遍历的 做法:对于那些能第二次到达的节点,逆序遍历其节点的左子树 的最右边界。  然后 最后补上整棵树的最右边界逆序。
void morrisPost(TreeNode* head){
	TreeNode* cur = head;
	TreeNode* mostRight = nullptr;
	while(cur != nullptr){
		mostRight = cur->left;
		//如果左指针为空 直接访问右节点
		if(mostRight != nullptr){
			//先 找到左子树的最右边节点
			while(mostRight->right != nullptr && mostRight->right != cur){
				mostRight = mostRight->right;
			}
			//然后看其有指针是否为空,为空的话,则表示cur第一次到达当前节点,否则为第二次
			if(mostRight->right == nullptr){
				mostRight->right = cur;
				cur = cur->left;
				continue;
			}else{
				mostRight->right = nullptr;
				printEdge(cur->left);
			}
		}
		cur = cur->right;
	}
	printEdge(head);
}


int main(){
	int arr[] = {1,2,3,4,5,6,7};
	int length = sizeof(arr)/sizeof(arr[0]);
	TreeNode* head = getTree(arr, length,0);

	//检验单链表下 ,printEDGE方法 有效
	// TreeNode* n1 = new TreeNode();
	// TreeNode* n2 = new TreeNode();
	// TreeNode* n3 = new TreeNode();
	// n1->setValue(1);
	// n2->setValue(2);
	// n3->setValue(3);
	// n1->right = n2;
	// n2->right = n3;
	// printEdge(n1);


	// morris(head);
	// morrisPre(head);
	// morrisIn(head);
	morrisPost(head);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值