20.9.4 二叉树的Morris遍历

tech2025-09-15  10

1.二叉树的Morris遍历(莫里斯)

使用递归和栈遍历二叉树,空间复杂度都是O(n),而Morris遍历通过使用叶子结点的null指针标记前驱结点,使空间复杂度降为了O(1)。

中序遍历:左子树-根节点-右子树 所以对于根节点,它的前驱结点是左子树的最后一个结点(即左子树的最右结点)

记当前的结点为cur 若cur有左子树,则pre=cur的左子树的最右结点 若pre的右孩子为空,则将它的右孩子指向cur,并进入cur的左孩子 若pre的右孩子是cur(上次访问时,上一种情况下设置的),则访问cur,删去pre的指向(还原原来的树结构),并进入cur的右孩子 若cur没有左子树,那么访问cur本身,然后进入cur的右孩子 class Solution { public: void morris(TreeNode* root) { TreeNode *pre = nullptr; while (root != nullptr) { if (root->left != nullptr) {// 当前结点root的左子树不为空 pre = root->left; while (pre->right != nullptr && pre->right != root) { pre=pre->right; }//找到左子树的最右结点,或者回到了root本身 if (pre->right == nullptr) {//右孩子为空 pre->right = root;//将它的右孩子指向cur(此处改变了树结构) root = root->left;//进入root的左孩子 }else { //此处访问root pre->right = nullptr;//恢复原来的树结构 root = root->right;//进入root的右孩子 } }else {//root没有左孩子 //此处访问root root = root->right;//进入root的右孩子 } } } };

先序遍历的情况类似:

void morris(TreeNode *root){ TreeNode *pre = nullptr; while (root != nullptr){ if (root->left != nullptr){ pre=root->left; while(pre->right != nullptr && pre->right != root){ pre = pre->right; } if (pre->right == nullptr){ pre->right = root; //此处访问root root = root->left; }else{ p2->right = nullptr; root = root->right; } }else{ //此处访问root root = root->right; } } }

后序遍历情况比较复杂:

当前结点cur的左子树不为空 左子树的最右孩子为空,则最右孩子pre的右结点指向当前结点cur,进入右子树 左子树的最右孩子指向当前结点,则删除这个指向,并“逆序访问左子树的所有右孩子”,进入右子树 解释:由于右孩子指向当前结点,说明这是第二次访问cur; 在两次访问之间,一定已经访问过cur的左孩子; 那么对于cur的左孩子,要么它的左孩子为空,要么已经“逆序访问过了左子树的所有右孩子”; 一层一层迭代下去,则对于左-右-根的顺序,“左”的部分一定已经访问完成了; 所以可以看成cur的左子树的每个结点都没有左孩子,所以逆序访问右孩子即可完成“右-根” 左子树为空,则进入右子树

如图: 第二次访问结点2时,访问路径1; 第二次访问结点1时,访问路径2; 第二次访问结点3时,访问路径3; 程序的最后访问根节点代表的路径4,结束

代码:

void morris(TreeNode *root){ TreeNode* pre,cur; cur=root; while (cur != nullptr){ if (cur>left != nullptr){ pre=cur->left; while(pre->right != nullptr && pre->right !=cur){ pre = pre->right; } if (pre->right == nullptr){//第一次访问cur,指向空 pre->right = cur; cur = cur->left; }else{//第二次访问cur,指向cur pre->right = nullptr; //逆序访问cur左子树的右边界 cur=cur->right; } }else//cur没有左孩子的情况 cur = cur->right; } //逆序访问root的右边界(注意这里在循环外面了) }

关于逆序访问,在保证空间复杂度为O(1)的情况下,需要先把右孩子们(可以看成链表了)转置,访问完成后再转置回来,这里不再给出代码了

最新回复(0)