Moris遍历实现了什么?
二叉树的前、中、后、层次遍历都需要额外建立栈和队列来完成,空间赋值度为O(h) , h为树的高度。而Moris遍历可以在同样时间复杂度为O(n) 内,但空间复杂度减小到O(1) 完成树的遍历。
实现关键?
把叶子结点的右指针改为指到它的祖先结点,达到“回溯”的效果。所以有左子树的结点会遍历到两次,其他结点遍历到一次。
实现细节?
Morris遍历细节 假设来到当前节点cur,开始时cur来到头节点位置
1)如果cur没有左孩子,cur向右移动(cur = cur.right)
else if(MostRight == NULL){
cur = cur->right;
}
2)如果cur有左孩子,找到左子树上最右的(为空或者指向自己终止)节点mostRight:
MostRight = cur->left;
if (MostRight != NULL){
while (MostRight ->right != NULL && MostRight->right != cur){
MostRight = MostRight->right;
}//inner while
3) a.如果mostRight的右指针指向空,让其指向cur, 然后cur向左移动(cur = cur.left)
if (MostRight->right == NULL){
MostRight->right = cur;
cur = cur->left;
}//inner if
b.如果mostRight的右指针指向cur,让其指向NULL, 然后cur向右移动(cur = cur.right)
else if(MostRight->right == cur){
MostRight->right = NULL;
cur = cur->right;
}//else if
4)cur为空时遍历停止
while (cur != NULL){
代码
moris先序遍历:
#pragma GCC optimize(3,"Ofast","inline")
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
#include<cstdlib>
#include<bitset>
#include<climits>
#include<functional>
#define F(i,s,t) for(int i=(s);i<=(t);i++)
#define D(i,s,t) for(int i=(s);i>=(t);i--)
#define dBug(i) printf("Value=%d\n",i)
#define ddBug(i,j) printf("Value=%d %d\n",i,j)
#define ed putchar('\n')
#define FO freopen("D:\\in.txt","r",stdin)
#define IOS cin.tie(0) ,cout.tie(0), cout.sync_with_stdio(0)
typedef long long ll;
//const int INF = 1 << 30;
//const double EPS = 1e-6;
//#define MX 102
//#define Mod 10000
using namespace std;
struct TreeNode
{
char val;
TreeNode *left;
TreeNode *right;
TreeNode(char x) :val(x), left(NULL), right(NULL){}
};
//构造二叉链表
TreeNode* CreatTreeNode(char str1[])
{
static TreeNode *root = NULL;
TreeNode *demoFather[600], *TreePoint;
int demoPoint = -1, curSurTree, strPoint = 0;
char temp;
temp = str1[strPoint]; //ch指向str1第一个字符,用[]访问
while (temp != '\0'){
switch (temp){
case '(':
demoPoint++;
demoFather[demoPoint] = TreePoint; //记录括号里元素的父节点
curSurTree = 1;
break;
case')':
demoPoint--;
break;
case',':
curSurTree = 2;
break;
default:
TreePoint = (TreeNode*)malloc(sizeof(TreeNode));
TreePoint->val = temp;
TreePoint->left = TreePoint->right = NULL;
if (root == NULL){
root = TreePoint;
}//if
else{
curSurTree == 1 ? demoFather[demoPoint]->left = TreePoint : demoFather[demoPoint]->right = TreePoint;
}//else
}//switch
strPoint++;
temp = str1[strPoint];
}//while
return root;
}
void MorisPre(TreeNode *root)
{
if (root == NULL) return;
TreeNode *cur = root; //当前节点
TreeNode *MostRight = NULL; //左孩子的最右边界点
while (cur != NULL){
MostRight = cur->left;
if (MostRight != NULL){ //cur 有左子树,找MostRight
while (MostRight ->right != NULL && MostRight->right != cur){
MostRight = MostRight->right;
}//inner while
if (MostRight->right == NULL){ //cur 的左子树没有遍历
cout << cur->val << " ";
MostRight->right = cur;
cur = cur->left;
}//inner if
else if(MostRight->right == cur){ //cur 的左子树遍历完成了
//cout << cur->val << " "; //第二次到达该结点
MostRight->right = NULL;
cur = cur->right;
}//else if
}//if
else if (MostRight == NULL){ //当cur 没有 左子树
cout << cur->val << " ";
cur = cur->right;
}
}//while
}
int main()
{
TreeNode *T = NULL;
char str1[1000]; //请输入括号法表示的二叉树序列
printf("请输入括号法表示的二叉树序列:\n");
scanf("%s", str1); //A(B(D, E), C(F, G))
printf("括号法表示的二叉树序列为:%s\n", str1);
T = CreatTreeNode(str1);
MorisPre(T);
return 0;
}
中序遍历:
与先序遍历不同的是打印父节点的时机。
void MorisIn(TreeNode *root)
{
if (root == NULL) return;
TreeNode *cur = root; //当前节点
TreeNode *MostRight = NULL; //左孩子的最右边界点
while (cur != NULL){
MostRight = cur->left;
if (MostRight != NULL){ //cur 有左子树,找MostRight
while (MostRight->right != NULL && MostRight->right != cur){
MostRight = MostRight->right;
}//inner while
if (MostRight->right == NULL){ //cur 的左子树没有遍历
//cout << cur->val << " "; //第一次到达左结点
MostRight->right = cur;
cur = cur->left;
}//inner if
else if (MostRight->right == cur){ //cur 的左子树遍历完成了
cout << cur->val << " "; //第二次到达该结点
MostRight->right = NULL;
cur = cur->right;
}//else if
}//if
else if (MostRight == NULL){ //当cur 没有 左子树
cout << cur->val << " ";
cur = cur->right;
}
}//while
}
moris后序遍历:
后序遍历也可由morris遍历加工得到,但是把处理时机放在,能够达到两次的节点并且是第二次到达的时候。
具体就是第二次到的时候,逆序打印左子树的右边界,最后记得逆序打印整颗树的右边界。
而逆序打印左子树的右边界是改了树的右孩子指针实现的:
void printReverse(TreeNode *root)
{
TreeNode *tail = ReverseEdge(root);
TreeNode *cur = tail;
while (cur != NULL){
cout << cur->val << " ";
cur = cur->right;
}//while
ReverseEdge(tail);
}
所有代码:
#pragma GCC optimize(3,"Ofast","inline")
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
#include<cstdlib>
#include<bitset>
#include<climits>
#include<functional>
#define F(i,s,t) for(int i=(s);i<=(t);i++)
#define D(i,s,t) for(int i=(s);i>=(t);i--)
#define dBug(i) printf("Value=%d\n",i)
#define ddBug(i,j) printf("Value=%d %d\n",i,j)
#define ed putchar('\n')
#define FO freopen("D:\\in.txt","r",stdin)
#define IOS cin.tie(0) ,cout.tie(0), cout.sync_with_stdio(0)
typedef long long ll;
//const int INF = 1 << 30;
//const double EPS = 1e-6;
//#define MX 102
//#define Mod 10000
using namespace std;
struct TreeNode
{
char val;
TreeNode *left;
TreeNode *right;
TreeNode(char x) :val(x), left(NULL), right(NULL){}
};
//构造二叉链表
TreeNode* CreatTreeNode(char str1[])
{
static TreeNode *root = NULL;
TreeNode *demoFather[600], *TreePoint;
int demoPoint = -1, curSurTree, strPoint = 0;
char temp;
temp = str1[strPoint]; //ch指向str1第一个字符,用[]访问
while (temp != '\0'){
switch (temp){
case '(':
demoPoint++;
demoFather[demoPoint] = TreePoint; //记录括号里元素的父节点
curSurTree = 1;
break;
case')':
demoPoint--;
break;
case',':
curSurTree = 2;
break;
default:
TreePoint = (TreeNode*)malloc(sizeof(TreeNode));
TreePoint->val = temp;
TreePoint->left = TreePoint->right = NULL;
if (root == NULL){
root = TreePoint;
}//if
else{
curSurTree == 1 ? demoFather[demoPoint]->left = TreePoint : demoFather[demoPoint]->right = TreePoint;
}//else
}//switch
strPoint++;
temp = str1[strPoint];
}//while
return root;
}
TreeNode *ReverseEdge(TreeNode *root)
{
if (root->right == NULL) return root;
TreeNode *pre = NULL;
TreeNode *next = NULL;
TreeNode *cur = root;
while (cur != NULL){
next = cur->right;
cur->right = pre;
pre = cur;
cur = next;
}//while
return pre;
}
void printReverse(TreeNode *root)
{
TreeNode *tail = ReverseEdge(root);
TreeNode *cur = tail;
while (cur != NULL){
cout << cur->val << " ";
cur = cur->right;
}//while
ReverseEdge(tail);
}
void MorisPost(TreeNode *root)
{
if (root == NULL) return;
TreeNode *cur = root; //当前节点
TreeNode *MostRight = NULL; //左孩子的最右边界点
while (cur != NULL){
MostRight = cur->left;
if (MostRight != NULL){ //cur 有左子树,找MostRight
while (MostRight->right != NULL && MostRight->right != cur){
MostRight = MostRight->right;
}//inner while
if (MostRight->right == NULL){ //cur 的左子树没有遍历
//cout << cur->val << " "; //第一次到达左结点
MostRight->right = cur;
cur = cur->left;
}//inner if
else if (MostRight->right == cur){ //cur 的左子树遍历完成了
MostRight->right = NULL;
printReverse(cur->left); //第二次到达该结点
cur = cur->right;
}//else if
}//if
else if (MostRight == NULL){ //当cur 没有 左子树
//cout << cur->val << " ";
cur = cur->right;
}
}//while
printReverse(root);// 打印根节点对应的最右边界
}
int main()
{
TreeNode *T = NULL;
char str1[1000]; //请输入括号法表示的二叉树序列
printf("请输入括号法表示的二叉树序列:\n");
scanf("%s", str1); //A(B(D,E),C(F,G))
printf("括号法表示的二叉树序列为:%s\n", str1);
T = CreatTreeNode(str1);
MorisPost(T);
return 0;
}