Morris遍历(值得学习的二叉树遍历方法)

Morris遍历

morris遍历是二叉树遍历算法的超强进阶算法,跟递归、非递归(栈实现)的空间复杂度,
morris遍历可以将非递归遍历中的空间复杂度降为O(1)。从而实现时间复杂度为O(N),而空间复杂度为O(1)的精妙算法。
morris遍历利用的是树的叶节点左右孩子为空(树的大量空闲指针),实现空间开销的极限缩减。

Morris遍历的实现原则

记作当前节点为cur。

  1. 如果cur无左孩子,cur向右移动(cur=cur.right)

  2. 如果cur有左孩子,找到cur左子树上最右的节点,记为mostright

    1. 如果mostright的right指针指向空,让其指向cur,cur向左移动(cur=cur.left)
    2. 如果mostright的right指针指向cur,让其指向空,cur向右移动(cur=cur.right)

实现以上的原则,即实现了morris遍历。

Morris遍历的实现原则

建立一种机制,对于没有左子树的节点只到达一次,对于有左子树的节点会到达两次

morris遍历的实例

一个树若按层遍历的结构为{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指针重新指回空

以上内容转载于:https://zhuanlan.zhihu.com/p/101321696

代码实现(Java)

/**
 * Morri 遍历
 * 	二叉树的前序、中序、后序遍历
 *	目的:遍历树的每一个节点,使用叶子节点大量空指针的特点,将叶子节点空子树指向它的后继节点
 */
package com.wsq.leetcode;

public class TraversingBinaryTrees {
	/**
	 * Morri二叉树先序遍历
	 * @param head
	 */
	public static void morrisPre(TreeNode head) {
		if(head == null) {
			return;
		}
		TreeNode cur = head;
		TreeNode mostRight = null;
		
		while(cur != null) {
			if(cur.left == null) {
				System.out.print(cur.val + " ");
				cur = cur.right;
			}else {
				mostRight = getMostRight(cur);
				if(mostRight.right == null) {
					mostRight.right = cur;
					System.out.print(cur.val + " ");
					cur = cur.left;
				}else {
					mostRight.right = null;
//					System.out.print(cur.val);
					cur = cur.right;
				}
			}
		}
		System.out.println();
	}
	/**
	 * Morri中序遍历
	 * @param head
	 */
	public static void morrisIn(TreeNode head) {
		if(head == null) {
			return;
		}
		TreeNode cur = head;
		TreeNode mostRight = null;
		
		while(cur != null) {
			if(cur.left == null) {
				System.out.print(cur.val + " ");
				cur = cur.right;
			}else {
				mostRight = getMostRight(cur);
				if(mostRight.right == null) {
					mostRight.right = cur;
					cur = cur.left;
				}else {
					mostRight.right = null;
					System.out.print(cur.val + " ");
					cur = cur.right;
				}
			}
		}
		System.out.println();
	}
	/**
	 * Morri 遍历 后序遍历
	 * @param head
	 */
	public static void morrisPos(TreeNode head) {
		if(head == null) {
			return;
		}
		TreeNode cur = head;
		TreeNode mostRight = null;
		
		while(cur != null) {
			if(cur.left == null) {
				cur = cur.right;
			}else {
				mostRight = getMostRight(cur);
				if(mostRight.right == null) {
					mostRight.right = cur;
					cur = cur.left;
				}else {
					// 当节点第二次别访问,则打印左子树最右分支路径边
					mostRight.right = null;
					printEdge(cur.left);
					cur = cur.right;
				}
			}
		
		}
		printEdge(head);
		System.out.println();
	}
	
	public static void printEdge(TreeNode node) {
		// 类似翻转链表
		TreeNode tail = reverseEdge(node);
		TreeNode cur = tail; 
		while(cur != null) {
			System.out.print(cur.val + " ");
			cur = cur.right;
		}
		// 恢复原来的形状
		reverseEdge(tail);
	}
	/**
	 *	翻转左子树的最右的边缘
	 *	类似链表的翻转
	 * @param node
	 * @return
	 */
	public static TreeNode reverseEdge(TreeNode node) {
		TreeNode pre = null;
		TreeNode next = null;
		while(node != null) {
			next = node.right;
			node.right = pre;
			pre = node;
			node = next;
		}
		return pre;
	}
	/**
	 *	获取后继节点为"当前节点"的节点
	 *	
	 * @param cur
	 * @return
	 */
	private static TreeNode getMostRight(TreeNode cur) {
		// TODO Auto-generated method stub
		TreeNode mostRight = cur.left;
		while(mostRight.right != null && mostRight.right != cur) {
			mostRight = mostRight.right;
		}
		return mostRight;
	}
	
	public static void main(String[] args) {
    	int[] a = {4,2,7,1,3,6,9};
    	TreeNode root = buildTree(a, 0);
    	System.out.println("前序遍历");
    	morrisPre(root);
    	System.out.println("中序遍历");
    	morrisIn(root);
    	System.out.println("后序遍历");
    	morrisPos(root);
	}
    
    public static TreeNode buildTree(int[] a, int pos) {
    	if(pos >= a.length) {
    		return null;
    	}
    	TreeNode root = new TreeNode(a[pos]);
    	root.left = buildTree(a, 2 * pos + 1);
    	root.right = buildTree(a, 2 * pos + 2);
    	return root;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值