Morris算法进行二叉树遍历

转自:blog.csdn.net/yangfeisc/article/details/45673947

二叉树作为计算机中的一个重要数据结构,在很多领域都会涉及到,而提到二叉树,我们首先想到的就是其3种遍历方式--前序、中序和后序,对于这三种遍历方式,我们很容易通过使用递归或者迭代(http://blog.csdn.NET/yangfeisc/article/details/44497429)的方式实现,时间复杂度为O(N)。但是这两种实现方式都需要使用堆栈进行节点信息的存储,即空间复杂度也是O(N)。

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

树的节点定义如下:

  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实现如下:

  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实现如下:

  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实现如下:

  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;  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值