算法笔记——左神进阶(3)Morris遍历:遇到二叉树遍历问题时,建议都采用

morris遍历解决问题:把二叉树遍历空间复杂度降为O(1)

之前学习的二叉树遍历,不管何种方式,时间复杂度如何,空间复杂度都为O(h),h为二叉树的高度。

morris算法将二叉树的空间复杂度变为O(1),同时时间复杂度不变,依旧保持O(n)

算法流程:

  1. 现在的当前节点记为cur,如果cur无左孩子,则cur向右移动,cur=cur.right
  2. 如果cur有左孩子,则找到左子树上最右的节点,记为mostright
    1)如果mostright的右指针指向空,则让其指向cur,此时cur向左移动,cur = cur.left
    2)如果mostright的左指针指向空,则让其指向空,cur向右移动

在这里插入图片描述
左边是需要遍历的树,右边是这个方法遍历二叉树,curr节点所经过的位置。

如果一个节点有左子树,Morris遍历会到达它两次;如果没有左子树,只遍历一次。当第二次到达某一个节点时,它的左子树上的节点都已经遍历完了。所以总的时间复杂度去除常数,依旧为O(N)。

Morris遍历理解:

Morris遍历极大地类似递归版的二叉树遍历,只是递归版是到一个节点,先去左,回来,再去右,回来,到达一个节点三次。
Morris遍历只到两次,它是用了左子树最右节点的右孩子是否为空或者指向自己来判断,对某个节点是第一次到达还是第二次到达

Morris遍历也可以针对非完全二叉树

【代码】

public static void morris(Node head){
	if(head == null){
		return ;
	}
	Node cur = head;
	Node 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;
			}
		}
		cur = cur.right;
	}
}

Morris遍历改为先序、中序、后序遍历

先序:第一次到达节点就打印

public static void morrisPre(Node head){
	if(head == null){
		return ;
	}
	Node cur = head;
	Node 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;
				System.out.print(cur.value+" ");
				cur = cur.left;
				continue;
			}else{
				mostRight.right = null;
			}
		}else{
		//一个节点如果没有左子树,只到达一次也是第一次,此时直接打印。
			System.out.print(cur.value + " ");
		}
		cur = cur.right;
	}
	System.out.println();
}

中序:打印完左子树准备向右的时候就打印当前节点

public static void morris(Node head){
	if(head == null){
		return ;
	}
	Node cur = head;
	Node 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;
			}
		}
		//打印完左子树准备向右的时候就打印当前节点
		System.out.print(cur.value + " ");
		cur = cur.right;
	}
	System.out.println();
}

后序:只关注每个有第二次返回的节点

  1. 每当有一个第二次到达的节点,就逆序打印其左子树的右边界;
  2. 整个树的二次返回节点的操作1打印完之后,打印整棵树的右边界。

这里证明了一下时间复杂度O(N)。。。但是没太理解

public static void morrisPos(Node head){
	if(head == null){
		return;
	}
	Node cur1 = head;
	Node cur2 = null;
	while(cur1 != null){
		cur2 = cur1.left;
		if(cur2 != null){
			while(cur2.right != null && cur2.right != cur1){
				cur2 = cur2.right;
			}
			if(cur2.right == null){
				cur2.right = cur1;
				cur1 = cur1.left;
				continue;
			}else{
				cur2.right = null;
				//整个节点的左子树的右边界进行逆序打印
				printEdge(cur1.left);
			}
		}
		cur1 = cur1.right;
	}
	//把整棵子树的右边界再逆序打印出来
	printEdge(head); 
	System.out.println();
}


//单独逆序打印有边界,用逆转链表的形式更改,然后最后调整回来
public static void printEdge(Node head) {
    Node tail = reverseEdge(head);
    Node cur = tail;
    while (cur != null) {
        System.out.print(cur.value + " ");
        cur = cur.right;
    }
    reverseEdge(tail);
}
发布了27 篇原创文章 · 获赞 4 · 访问量 812

猜你喜欢

转载自blog.csdn.net/qq_25414107/article/details/104701472
今日推荐