当前位置:网站首页>Morris遍历
Morris遍历
2022-06-24 19:45:00 【今天也要写bug、】
传统的遍历方式不管是用递归实现还是用栈实现,其遍历的空间复杂度都是O(N),而Morris遍历能够做到只用O(1)的空间复杂度,并且其时间复杂度和传统方式一样都是O(N)。
Morris遍历介绍
Morris遍历利用原树中大量的空闲指针的方式,来达到节省空间的目的。
Morris遍历的规则为:
假设当前节点cur,开始时cur来到头结点的位置
- 如果cur没有左孩子,cur向右移动(cur=cur->right)
- 如果cur有左孩子,找到左子树上的最右节点mostRight
1).如果mostRight的右指针指向空,让其指向cur,然后cur向左移动(cur=cur->left)
2).如果mostRight的右指针指向cur,让其指向nullptr,然后cur向右移动(cur=cur->right) - cur为空时,停止遍历。
class Node
{
public:
Node(int _value)
{
value = _value;
left = nullptr;
right = nullptr;
}
int value;
Node* left;
Node* right;
};
void morris(Node* head)
{
if (head == nullptr)
{
return;
}
Node* cur = head;
//mostRight为cur左树的最右节点
Node* mostRight = nullptr;
while (cur != nullptr)
{
mostRight = cur->left;
//如果cur没有左树,那么cur会直接向右移动
if (mostRight != nullptr)
{
//找cur左树的最右节点,当为空或者等于cur时停止
while (mostRight->right != nullptr && mostRight->right != cur)
{
mostRight = mostRight->right;
}
//如果为空,说明第一次到最右节点,此时将指针指向cur
//然后cur向左子树移动
if (mostRight->right == nullptr)
{
mostRight->right = cur;
cur = cur->left;
continue;
}
//此时mostRight->right == cur
//说明第二次到达该节点,恢复该节点的指针
else
{
mostRight->right = nullptr;
}
}
cur = cur->right;
}
}

Morris先序遍历
先序遍历的顺序是根左右,由于Morris遍历可能会到达一个节点两次,所以我们需要在第一次到达节点时就打印。
void morrisPre(Node* head)
{
if (head == nullptr)
{
return;
}
Node* cur = head;
//mostRight为cur左树的最右节点
Node* mostRight = nullptr;
while (cur != nullptr)
{
mostRight = cur->left;
//如果cur没有左树,那么cur会直接向右移动
if (mostRight != nullptr)
{
//找cur左树的最右节点,当为空或者等于cur时停止
while (mostRight->right != nullptr && mostRight->right != cur)
{
mostRight = mostRight->right;
}
//如果为空,说明第一次到最右节点,此时将指针指向cur
//然后cur向左子树移动
if (mostRight->right == nullptr)
{
cout << cur->value << endl;
mostRight->right = cur;
cur = cur->left;
continue;
}
//此时mostRight->right == cur
//说明mostRight第二次到达该节点,恢复该节点的指针
else
{
mostRight->right = nullptr;
}
}
//cur没有左树的情况,直接打印cur,然后移动cur到右子树上
else
{
cout << cur->value << endl;
}
cur = cur->right;
}
}
Morris中序遍历
中序遍历遵循左根右的原则,对于一个节点,第二次到达时打印。
void morrisIn(Node* head)
{
if (head == nullptr)
{
return;
}
Node* cur = head;
//mostRight为cur左树的最右节点
Node* mostRight = nullptr;
while (cur != nullptr)
{
mostRight = cur->left;
//如果cur没有左树,那么cur会直接向右移动
if (mostRight != nullptr)
{
//找cur左树的最右节点,当为空或者等于cur时停止
while (mostRight->right != nullptr && mostRight->right != cur)
{
mostRight = mostRight->right;
}
//如果为空,说明第一次到最右节点,此时将指针指向cur
//然后cur向左子树移动
if (mostRight->right == nullptr)
{
mostRight->right = cur;
cur = cur->left;
continue;
}
//此时mostRight->right == cur
//说明mostRight第二次到达该节点,恢复该节点的指针
else
{
mostRight->right = nullptr;
}
}
//由于第一次的时候mostRight->right = cur;
//因此左子树的cur可以到达该节点两次,第二次时进行打印
cout << cur->value << endl;
cur = cur->right;
}
}
Morris后序遍历
后序遍历是左右根的顺序,由于我们没有设置父指针,因此没法从右子树往根节点打印。除非使用栈结构逆序,但是这违背了空间复杂度为O(1)的初衷。
因此,我们需要把链表进行翻转,将右指针指向它的父亲节点,打印完以后再改回来。
//翻转链表
Node* reverseEdge(Node* from)
{
Node* pre = nullptr;
Node* next = nullptr;
while (from != nullptr)
{
next = from->right;
from->right = pre;
pre = from;
from = next;
}
return pre;
}
void printEdge(Node* head)
{
//将右子树的最右边看成一个链表
//将链表翻转
Node* tail = reverseEdge(head);
Node* cur = tail;
while (cur != nullptr)
{
cout << cur->value << endl;
cur = cur->right;
}
//链表再翻转回去
reverseEdge(tail);
}
void morrisPos(Node* head)
{
if (head == nullptr)
{
return;
}
Node* cur = head;
//mostRight为cur左树的最右节点
Node* mostRight = nullptr;
while (cur != nullptr)
{
mostRight = cur->left;
//如果cur没有左树,那么cur会直接向右移动
if (mostRight != nullptr)
{
//找cur左树的最右节点,当为空或者等于cur时停止
while (mostRight->right != nullptr && mostRight->right != cur)
{
mostRight = mostRight->right;
}
//如果为空,说明第一次到最右节点,此时将指针指向cur
//然后cur向左子树移动
if (mostRight->right == nullptr)
{
mostRight->right = cur;
cur = cur->left;
continue;
}
//此时mostRight->right == cur
//说明第二次到达该节点,恢复该节点的指针
else
{
mostRight->right = nullptr;
//逆序打印一棵树左子树的右边界
printEdge(cur->left);
}
}
cur = cur->right;
}
//逆序打印整棵树的右边界
printEdge(head);
}
边栏推荐
- websocket学习
- R language uses the multinom function of NNET package to build an unordered multi classification logistic regression model, and uses exp function and coef function to obtain the corresponding odds rat
- 二分查找数组下标
- 379. 捉迷藏
- File contains vulnerability issues
- R语言使用nnet包的multinom函数构建无序多分类logistic回归模型、使用exp函数和coef函数获取模型中每个变量(自变量改变一个单位)对应的优势比(odds ratio)
- The dplyr package select function of R language moves the specified data column in the dataframe data to the first column (the first column) in the dataframe data column
- Whereabouts computer desktop small arrow
- OpenSSL SSL_ read: Connection was reset, errno 10054
- jar中没有主清单属性
猜你喜欢

Concurrent shared model management

From client to server

Fibonacci

RT-thread使用rt-kprintf

22map introduction and API

Installing IBM CPLEX academic edition | CONDA installing CPLEX

从客户端到服务器

Yyds dry goods inventory tells us 16 common usage scenarios of redis at one go

RT thread uses RT kprintf

Ningde times will increase RMB 45billion: Hillhouse subscribes RMB 3billion and Zeng Yuqun still controls 23% of the equity
随机推荐
MySQL 表的增删查改
R语言使用nnet包的multinom函数构建无序多分类logistic回归模型、使用exp函数和coef函数获取模型中每个变量(自变量改变一个单位)对应的优势比(odds ratio)
斐波那契
Listen to the markdown file and hot update next JS page
[JS] - [string - application] - learning notes
2021-2022中国金融数字化“新”洞察行业研究报告
golang map clear
The dplyr package select function of R language moves the specified data column in the dataframe data to the first column (the first column) in the dataframe data column
7-9 寻宝路线
Latest development of jetpack compose
常用正则表达式
[JS] - [array, stack, queue, linked list basics] - Notes
golang convert json string to map
当初吃土建起来的“中台”,现在为啥不香了?
Record the range of data that MySQL update will lock
Common regular expressions
Installation and deployment of ganglia
Building Survey [1]
OpenSSL SSL_ read: Connection was reset, errno 10054
golang map clear