Morris
本篇文章为Binary-Tree中遍历二叉树的进阶版本
二叉树的普通遍历有递归和迭代的方式 递归会有递归空间开销 迭代会有通过栈实现的空间开销 而Morris遍历可以将非递归遍历中的空间复杂度降为O(1) 利用的则是二叉树中大量的叶结点的左右孩子为空(空闲指针) 来实现的极限缩减空间 思想与线索二叉树很像 但不像线索二叉树一样需要开辟两个指针域
Morris遍历规则
当前结点为cur
若cur无左孩子 cur=cur->right
若cur有左孩子 则找到cur的左子树上最右侧的结点 记为mostright mostright初值为cur->left 循环找到最右侧结点再停止
若mostright->right=NULL 就使mostright->right=cur 并同时cur=cur->left
若mostright->right=cur 就使mostright->right=NULL 并同时cur=cur->right
从结果上来观察 这个mostright结点是cur结点的前驱结点时 cur就借由这个前驱结点的桥梁回到上一个遍历点
Morris遍历实例
针对层序遍历结构为{1,2,3,4,5,-1,6}的二叉树实例 来进行Morris遍历
1 2 3 4 5
| 1 / \ 2 3 / \ \ 4 5 6
|
Ⅰ cur=1 存在左孩子 cur左子树上最右侧的结点为5 则mostright=5 并且5->right=NULL 所以使5->right=cur=1 同时 cur=cur->left=2
Ⅱ cur=2 存在左孩子 cur左子树上最右侧的结点为4 则mostright=4 并且4->right=NULL 所以使得4->right=cur=2 同时 cur=cur->left=4
Ⅲ cur=4 不存在左孩子 所以cur=cur->right 此时4->right=2 即cur=2
Ⅳ cur=2 存在左孩子 cur左子树上最右侧的结点为4 则mostright=4 此时4->right=2 所以使得4->right=NULL 同时cur=cur->right=5
Ⅴ cur=5 不存在左孩子 所以cur=cur-right 此时5->right=1 即cur=1
Ⅵ cur=1 存在左孩子 cur左子树上最右侧的结点为5 则mostright=5 并且5->right=1 所以使5->right=NULL 同时cur=cur->right=3
Ⅶ cur=3 不存在左孩子 cur=cur->right=6
Ⅷ cur=6 不存在左孩子 结点遍历完毕
Morris遍历代码
先序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| vector<int> Morris_preOrder(TreeNode* root){ vector<int> res; if(!root) return res; TreeNode* cur=root; TreeNode* mostright=NULL; while(cur!=NULL){ mostright=cur->left; if(mostright!=NULL){ while(mostright->right!=NULL&&mostright->right!=cur) mostright=mostright->right; if(mostright->right==NULL){ mostright->right=cur; res.push_back(cur->val); cur=cur->left; continue; } else mostright->right=NULL; } else res.push_back(cur->val); cur=cur->right; } return res; }
|
中序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| vector<int> Morris_inOrder(TreeNode* root){ vector<int> res; if(!root) return res; TreeNode* cur=root; TreeNode* mostright=NULL; while(cur!=NULL){ mostright=cur->left; if(mostright!=NULL){ while(mostright->right!=NULL&&mostright->right!=cur) mostright=mostright->right; if(mostright->right==NULL){ mostright->right=cur; cur=cur->left; continue; } else{ mostright->right=NULL; } } res.push_back(cur->val); cur=cur->right; } return res; }
|
PS: 中序遍历代码中的注释 是第一次设计Morris中序遍历时写的代码 对于结点的输出情况 虽然结果相同 但是代码结构上多余了一个else的判断后输出 过于冗余 因此进行修改 但是第一次设计的代码虽然冗余但更为易读 所以以注释的形式保留下来
后序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| class PostOrder{ public: TreeNode* reverseEdge(TreeNode* root){ TreeNode* pre=NULL; TreeNode* next=NULL; while(root!=NULL){ next=root->right; root->right=pre; pre=root; root=next; } return pre; } void printEdge(TreeNode* root){ TreeNode* tail=reverseEdge(root); TreeNode* cur=tail; while(cur!=NULL){ res.push_back(cur->val); cur=cur->right; } reverseEdge(tail); } vector<int> Morris_postOrder(TreeNode* root){ if(!root) return res; TreeNode* cur=root; TreeNode* mostright=NULL; while(cur!=NULL){ mostright=cur->left; if(mostright!=NULL){ while(mostright->right!=NULL&&mostright->right!=cur) mostright=mostright->right; if(mostright->right==NULL){ mostright->right=cur; cur=cur->left; continue; } else{ mostright->right=NULL; printEdge(cur->left); } } cur=cur->right; } printEdge(root); return res; }
private: vector<int> res; };
int main(){ vector<int> nums={1,2,3,4,5,-1,6}; TreeNode* root=createTree(nums,nums.size(),0); PostOrder ans; vector<int> res=ans.Morris_postOrder(root); for(auto x:res) cout<<x<<" "; }
|