基本模板:
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;
}