Java实现Morris遍历二叉树

Morris遍历二叉树是遍历二叉树的神级方法,它的时间复杂度仅为O(n),空间复杂度为O(1)。

主要包含以下两大步骤:

1、拿到一个节点,如果该节点无左子树,那么节点指向它的右节点。

2、如果这个节点有左子树,找到左子树的最右节点。

       a、如果最右节点指向null,则让最右节点指向当前节点,并将该目标节点向左孩子移动。

       b、如果最右节点已经指向该节点,则让最右节点指向null,并将该目标节点向右孩子移动。

一直按照上述两大步骤递归,直到遍历完所有节点。

代码如下所示:

package problems_2017_08_21;


/**
 * @author islongfei
 * 遍历二叉树的神级方法
      【题目】
       给定一棵二叉树的头节点head,完成二叉树的先序、中序和后序遍历。如果二叉树的节点数为N,
       要求时间复杂度为O(N),额外空间复杂度为O(1)。

 *
 */
public class Problem_01_MorrisTraversal {

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

		public Node(int data) {
			this.value = data;
		}
	}
	
	/**
	 * Morris 先序遍历二叉树(第一次发现节点就打印:①左子树的右孩子为空时打印 ②无左子树时打印)
	 * @param head	
	 */
	
	public static void morrisPre(Node head) {
		if (head == null) {
			return;
		}
		Node cur1 = head;//每次得到的当前node
		Node cur2 = null;//当前node左子树最右的节点
		while (cur1 != null) {
			cur2 = cur1.left;
			if (cur2 != null) {
				while (cur2.right != null && cur2.right != cur1) {//找到左子树最右节点
					cur2 = cur2.right;
				}
				
				if (cur2.right == null) {   //最右点的右指针指向null,
					cur2.right = cur1;      //右指针指向当前节点
					System.out.print(cur1.value + " ");
					cur1 = cur1.left;       //当前node向左孩子移动
					continue;
				} else {                    //最右点的右指针已经指向当前节点,让它指向null
					cur2.right = null;
				}
			} else {
				System.out.print(cur1.value + " ");
			}
			cur1 = cur1.right;              //当前node向右孩子移动
		}
		System.out.println();
	}

	/**
	 * Morris中序遍历二叉树(node每次往右移之前打印节点)
	 * @param head
	 */
	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();
	}

	/**
	 * Morris后序遍历二叉树(当第二次回到node时,逆序打印左子树的右边界,在打印整树的右边界)
	 * @param head
	 */
	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;
	}

	// for test -- print tree
	public static void printTree(Node head) {
		System.out.println("Binary Tree:");
		printInOrder(head, 0, "H", 17);
		System.out.println();
	}

	public static void printInOrder(Node head, int height, String to, int len) {
		if (head == null) {
			return;
		}
		printInOrder(head.right, height + 1, "v", len);
		String val = to + head.value + to;
		int lenM = val.length();
		int lenL = (len - lenM) / 2;
		int lenR = len - lenM - lenL;
		val = getSpace(lenL) + val + getSpace(lenR);
		System.out.println(getSpace(height * len) + val);
		printInOrder(head.left, height + 1, "^", len);
	}

	public static String getSpace(int num) {
		String space = " ";
		StringBuffer buf = new StringBuffer("");
		for (int i = 0; i < num; i++) {
			buf.append(space);
		}
		return buf.toString();
	}

	public static void main(String[] args) {
		Node head = new Node(4);
		head.left = new Node(2);
		head.right = new Node(6);
		head.left.left = new Node(1);
		head.left.right = new Node(3);
		head.right.left = new Node(5);
		head.right.right = new Node(7);
		printTree(head);
		morrisPre(head);
		morrisIn(head);
		morrisPos(head);
		printTree(head);

	}

}



根据打印的位置不同可以分为以下三种输出的遍历方法:

中序遍历:将目标节点往右移之前打印该节点。

先序遍历:第一次发现节点是就打印(1、左子树右孩子为空时 2、无左子树时)。

后续遍历:第二次回到目标节点,逆序打印左子树的右边界,再去打印整个树的右边界。


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值