算法与数据结构进阶课第六节笔记

Morris遍历及其相关扩展

一种遍历二叉树的方式,并且时间复杂度O(N),额外空间复杂度O(1)
 
通过利用原树中大量空闲指针的方式,达到节省空间的目的

当前节点cur,一开始cur来到树头

1、cur无左树,cur = cur.right

2、cur 有左树,找到左树最右节点mostRight

(1)、mostRight 的右指针指向null, mostRight.right = cur, cur = cur.left

(2)、mostRight 的右指针指向 cur,mostRight.right = null, cur = cur.right

3、cur = null 时 停

用右指针为null表示首次来到cur,cur.right = cur 表示第二次来到

 

   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) { // 有左树的情况下,找到左树上真实的最右节点
				// 第一次的时候会指向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 一定是等于 cur
					mostRight.right = null;
				}
			}
			cur = cur.right;
		}
	}

Morris整个流程就是上面的代码

可以发现,有左子树的结点会遍历两次,没有的只会有一次

那么先序就是在 第一次来到时打印,中序就是在第二次来到时(只要这个节点往右移动之前)打印,只到一次的遇到就打印

中序

public static void morrisIn(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;
				}
			}
			System.out.print(cur1.value + " ");
			cur1 = cur1.right;
		}
		System.out.println();
	}

先序

public static void morrisPre(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;
					// 说明有左树的结点,第一次达到
					System.out.print(cur1.value + " ");
					cur1 = cur1.left;
					continue;
				} else {
					cur2.right = null;
				}
			} else { // 无左树的,就要往右移动了,所以在这个加个打印,只为到达一次的结点
				System.out.print(cur1.value + " ");
			}
			cur1 = cur1.right;
		}
		System.out.println();
	}

后序遍历:

在有左树的结点第二次到达时,逆序打印左树的右边界,最后剩余head,逆序打印head的右边界

因为要O(1)利用单链表的反转,打完后再调整回来

    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);
	}

        public static Node reverseEdge(Node from) {
		Node pre = null;
		Node next = null;
		while (from != null) {
			next = from.right;
			from.right = pre;
			pre = from;
			from = next;
		}
		return pre;
	}

判断是否是搜索二叉树,记录前值,之前的打印时机,变成判断是否递增

    public static boolean isBST(Node head) {
		if (head == null) {
			return true;
		}
		Node cur1 = head;
		Node cur2 = null;
		Integer pre = 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;
				}
			}
			if (pre != null && pre >= cur1.value){
				return false;
			}
			pre = cur1.value;
			cur1 = cur1.right;
		}
		return true;
	}

二叉树的最小高度

递归:

       public static class Node {
		public int val;
		public Node left;
		public Node right;

		public Node(int x) {
			val = x;
		}
	}

	public static int minHeight1(Node head) {
		if (head == null) {
			return 0;
		}
		return p(head);
	}

	public static int p(Node x) {
		if (x.left == null && x.right == null) {
			return 1;
		}
		// 左右子树起码有一个不为空
		int leftH = Integer.MAX_VALUE;
		if (x.left != null) {
			leftH = p(x.left);
		}
		int rightH = Integer.MAX_VALUE;
		if (x.right != null) {
			rightH = p(x.right);
		}
		return 1 + Math.min(leftH, rightH);
	}

morris

        // 根据morris遍历改写
	public static int minHeight2(Node head) {
		if (head == null) {
			return 0;
		}
		Node cur = head;
		Node mostRight = null;
		int curLevel = 0;
		int minHeight = Integer.MAX_VALUE;
		while (cur != null) {
			mostRight = cur.left;
			if (mostRight != null) {
				int rightBoardSize = 1;
				while (mostRight.right != null && mostRight.right != cur) {
					rightBoardSize++;
					mostRight = mostRight.right;
				}
				if (mostRight.right == null) { // 第一次到达
					curLevel++;
					mostRight.right = cur;
					cur = cur.left;
					continue;
				} else { // 第二次到达
					if (mostRight.left == null) {
						// right 是空,左也空,表明是叶节点,取最小
						minHeight = Math.min(minHeight, curLevel);
					}
					// 重新更新cur的层数
					curLevel -= rightBoardSize;
					mostRight.right = null;
				}
			} else { // 只有一次到达
				curLevel++;
			}
			cur = cur.right;
		}
		int finalRight = 1;
		// 最后head的右边界需要计算后比对
		cur = head;
		while (cur.right != null) {
			finalRight++;
			cur = cur.right;
		}
		if (cur.left == null && cur.right == null) {
			minHeight = Math.min(minHeight, finalRight);
		}
		return minHeight;
	}

如果是来到X节点,需要左树的信息,右树的信息,不能使用morris

如果是来到X节点,左树的信息只需要使用一次,就可以使用morris

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值