神级遍历——Morris算法及其应用

Morris  二叉树(Binary tree)是树形结构的一个重要类型。提到二叉树,我们首先想到的就是其3种遍历方式–前序、中序和后序,对于这三种遍历方式,我们很容易通过使用递归或者迭代的方式实现,时间复杂度为O(N)。但是这两种实现方式都需要使用堆栈进行节点信息的存储,即空间复杂度也是O(N)。  但是还有一种更为巧妙的遍历方法–Morris算法,该算法的时间复杂度也是O(N),但是空间复杂度却能达到最优的O(1)Morris遍历的实现:  记当前节点 cur ,一开始 cur 指向整棵树的头节
摘要由CSDN通过智能技术生成

Morris

  二叉树(Binary tree)是树形结构的一个重要类型。提到二叉树,我们首先想到的就是其3种遍历方式–前序、中序和后序,对于这三种遍历方式,我们很容易通过使用递归或者迭代的方式实现,时间复杂度为O(N)。但是这两种实现方式都需要使用堆栈进行节点信息的存储,即空间复杂度也是O(N)
  但是还有一种更为巧妙的遍历方法–Morris算法,该算法的时间复杂度也是O(N),但是空间复杂度却能达到最优的O(1)

Morris遍历的实现

  记当前节点 cur ,一开始 cur 指向整棵树的头节点

  1. 如果cur无左子树,cur向右移动(cur=cur.right)
  2. 如果cur有左子树,找到cur左子树上最右的节点,记为mostright
    ①如果mostright的right指针指向空,让其指向cur(mostRight.ringht = cur ),cur向左移动(cur=cur.left)
    ②如果mostright的right指针指向cur,让其指向空(mostRight.ringht = null),cur向右移动(cur=cur.right)

对于没有左子树的节点只到达一次,对于有左子树的节点会到达两次

实例:

一个树若按层遍历的结构为{1,2,3,4,5,6,7},即该树为满二叉树,头结点值为1,左右孩子为2,3,叶节点为4,5,6,7
请添加图片描述

morris遍历来遍历该树过程:

1)首先cur来到头结点1,按照morris原则的第二条第一点,它存在左孩子,cur左子树上最右的节点为5,它的right指针指向空,所以让其指向1,cur向左移动到2。

2)2有左孩子,且它左子树最右的节点4指向空,按照morris原则的第二条第一点,让4的right指针指向2,cur向左移动到4

3)4不存在左孩子,按照morris原则的第一条,cur向右移动,在第二步中,4的right指针已经指向了2,所以cur会回到2

4)重新回到2,有左孩子,它左子树最右的节点为4,但是在第二步中,4的right指针已经指向了2,不为空。所以按照morris原则的第二条第二点,2向右移动到5,同时4的right指针重新指向空

5)5不存在左孩子,按照morris原则的第一条,cur向右移动,在第一步中,5的right指针已经指向了1,所以cur会回到1

6)cur回到1,回到头结点,左子树遍历完成,1有左孩子,左子树上最右的节点为5,它的right指针指向1,按照morris原则的第二条第二点,1向右移动到3,同时5的right指针重新指回空

……

当到达最后一个节点7时,按照流程下来,此时7无左右孩子,遍历结束。

	public static void morris(Node head) {
   
	    if(head == null){
   
	        return;
	    }
	    Node cur = head;
	    Node mostRight = null;
	    while (cur != null){
   	    	
	    	// cur 有没有左树
	        mostRight = cur.left;
	        if(mostRight != null){
    // 有左树的情况下
	        	// 找到cur左树上,真实的最右
	            while (mostRight.right !=null && mostRight.right != cur){
   
	                mostRight = mostRight.right;
	            }	      
	            //从while中出来,mostRight一定是cur左树上的最右节点
	            if(mostRight.right == null){
   
	                mostRight.right = cur;
	                cur = cur.left;
	                continue;
	            }else {
     // mostRight.right != null -> mostRight.right == cur
	                mostRight.right = null;
	            }
	        }
	        cur = cur.right;
	    }
	    System.out.println();
	}

前、中、后序遍历:

如上实例中,依次到达的节点为:1 2 4 2 5 1 3 6 3 7

先序遍历:第一次到达就打印
1 2 4 2 5 1 3 6 3 7
√ √ √ x √ x √ √ x √
即先序遍历结果为:1 2 4 5 3 6 7

中序遍历:对于只能到达一次的节点直接打印,对于能到达第二次的节点,第二次打印
1 2 4 2 5 1 3 6 3 7
x x √ √ √ √ x √ √ √
即先序遍历结果为:4 2 5 1 6 3 7

后序遍历:到达该节点第二次的时候打印该节点左子树的逆序右边界

打印时机:能回到自己两次且第二次回到自己的时候
但是不是打印自己

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值