Morris遍历

Morris算法,该算法的时间复杂度也是O(N),但是空间复杂度却能达到最优的O(1)。下面根据二叉树的三种遍历方式详细介绍Morris算法。

树的节点定义如下:

[java]  view plain  copy
  1. public class TreeNode {  
  2.     int val;  
  3.         TreeNode left;  
  4.         TreeNode right;  
  5.         TreeNode(int x) {   
  6.             val = x;   
  7.     }  
  8.     }  

一、中序遍历:

算法步骤: 

1. 如果当前节点的左子节点为空时,输出当前节点,并将当前节点置为该节点的右子节点;

2. 如果当前节点的左子节点不为空,找到当前节点左子树的最右节点(该节点为当前节点中序遍历的前驱节点);

2.1. 如果最右节点的右指针为空(right=null),将最右节点的右指针指向当前节点,当前节点置为其左子节点;

2.2. 如果最右节点的右指针不为空,将最右节点右指针重新置为空(恢复树的原状),输出当前节点,并将当前节点置为其右节点;

3. 重复1~2,直到当前节点为空。


下图显示了Morris算法中序遍历的过程。


Java实现如下:

[java]  view plain  copy
  1. public List<Integer> Morris_InOrder(TreeNode root) {  
  2.     List<Integer> res = new ArrayList<>();  
  3.     if(root == null)  
  4.         return res;  
  5.     TreeNode cur = root;  
  6.     while(cur != null) {  
  7.         if(cur.left == null) {  
  8.             res.add(cur.val);  
  9.             cur = cur.right;  
  10.         } else {  
  11.             TreeNode tmp = cur.left;  
  12.             while(tmp.right != null && tmp.right != cur)  
  13.                 tmp = tmp.right;  
  14.             if(tmp.right == null) {  
  15.                 tmp.right = cur;  //找到当前节点的前驱节点  
  16.                 cur = cur.left;  
  17.             } else {  
  18.                 res.add(cur.val);  
  19.                 tmp.right = null;  //恢复二叉树  
  20.                 cur = cur.right;  
  21.             }  
  22.         }  
  23.     }  
  24.     return res;  
  25. }  


二、前序遍历,前序遍历的基本思想和中序遍历很相似,只有输出顺序发生变化。

算法步骤:

1. 如果当前节点的左子节点为空时,输出当前节点,并将当前节点置为该节点的右子节点;

2. 如果当前节点的左子节点不为空,找到当前节点左子树的最右节点(该节点为当前节点中序遍历的前驱节点);

2.1. 如果最右节点的右指针为空(right=null),将最右节点的右指针指向当前节点,并输出当前节点(在此处输出),当前节点置为其左子节点;

2.2. 如果最右节点的右指针不为空,将最右节点右指针重新置为空(恢复树的原状),并将当前节点置为其右节点;

3. 重复1~2,直到当前节点为空。


下图显示了Morris算法前序遍历的过程:


Java实现如下:

[java]  view plain  copy
  1. public List<Integer> Morris_PreOrder(TreeNode root) {  
  2.     List<Integer> res = new ArrayList<>();  
  3.     if(root == null)  
  4.         return res;  
  5.     TreeNode cur = root;  
  6.     while(cur != null) {  
  7.         if(cur.left == null) {  
  8.             res.add(cur.val);  
  9.             cur = cur.right;  
  10.         } else {  
  11.             TreeNode tmp = cur.left;  
  12.             while(tmp.right != null && tmp.right != cur)  
  13.                 tmp = tmp.right;  
  14.             if(tmp.right == null) {  
  15.                 res.add(cur.val); //输出当前节点  
  16.                 tmp.right = cur;  //找到当前节点的前驱节点  
  17.                 cur = cur.left;  
  18.             } else {  
  19.                 tmp.right = null;  //恢复二叉树  
  20.                 cur = cur.right;  
  21.             }  
  22.         }  
  23.     }  
  24.     return res;  
  25. }  


、后序遍历:后序遍历较前两者比较麻烦,需要建立一个临时节点,并令该节点的左子节点为root,并且需要一个子过程,倒序输出某两个节点之间路径上的各个节点。

算法步骤:

1. 如果当前节点的左子节点为空时,则将其右子节点作为当前节点;

2. 如果当前节点的左子节点不为空,找到当前节点左子树的最右节点(该节点为当前节点中序遍历的前驱节点);

2.1. 如果最右节点的右指针为空(right=null),将最右节点的右指针指向当前节点,当前节点置为其左子节点;

2.2. 如果最右节点的右指针不为空,将最右节点右指针重新置为空(恢复树的原状),倒序输出从当前节点的左子节点到该最右节点路径上的所有节点,并将当前节点置为其右节点;

3. 重复1~2,直到当前节点为空。


下图显示了Morris算法后序遍历过程(其中虚线框内的节点为临时节点):


Java实现如下:

[java]  view plain  copy
  1. public List<Integer> Morris_PostOrder(TreeNode root) {  
  2.     List<Integer> res = new ArrayList<>();  
  3.     if(root == null)  
  4.         return res;  
  5.     TreeNode virNode = new TreeNode(-1);  //建立临时节点  
  6.     virNode.left = root;    //设置临时节点的左子节点为根节点  
  7.     TreeNode cur = virNode;  
  8.     while(cur != null) {  
  9.         if(cur.left == null) {  
  10.             cur = cur.right;  
  11.         } else {  
  12.             TreeNode tmp = cur.left;  
  13.             while(tmp.right != null && tmp.right != cur)  
  14.                 tmp = tmp.right;  
  15.             if(tmp.right == null) {  
  16.                 tmp.right = cur;  //找到当前节点的前驱节点  
  17.                 cur = cur.left;  
  18.             } else {  
  19.                 tmp.right = null;  //恢复二叉树  
  20.                 TreeNode t = cur.left;    
  21.                 List<Integer> tmpList = new ArrayList<>();  
  22.                 while(t != null) {  //倒序输出当前节点左子节点到当前节点前驱节点路径上的所有节点  
  23.                     tmpList.add(0, t.val);  
  24.                     t = t.right;  
  25.                 }  
  26.                 res.addAll(tmpList);  
  27.                 cur = cur.right;  
  28.             }  
  29.         }  
  30.     }  
  31.     return res;  
  32. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值