二叉树遍历系列02-Morris遍历
一、引言
二、递归序
三、Morris遍历算法简介
3.1 Morris遍历过程简介
- 设置游标指针cur = root,如果cur == null,转2,否则转3
- 返回先序遍历结果
- 如果cur有左孩子,转4,否则转5
- 设置mostRight = cur.left,并且让mostRight一直往右走,即mostRight = mostRight.right,直到mostRight.right == null 或者mostRight.right == cur为止,转6
- cur = cur.right
- 如果cur的最右结点的右孩子为null,那么将mostRight.right = cur,cur = cur.left,否则转7
- most.right = null,转5
代码实现:
private void morris(TreeNode<V> root) {
TreeNode<V> cur = root;
while (cur != null) {
if (cur.left != null) {
TreeNode<V> mostRight = cur.left;
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遍历:
3.2 基于Morris遍历加工出先序序列
private void morris(TreeNode<V> root) {
TreeNode<V> cur = root;
while (cur != null) {
if (cur.left != null) {
TreeNode<V> mostRight = cur.left;
while (mostRight.right != null && mostRight.right != cur) {
mostRight = mostRight.right;
}
if (mostRight.right == null) {
// 这个if分支是cur第一次被访问的时候,因为mostRight.right为null
// 如果cur是第二次被访问,那么cur一定是通过这个mostRight.right回来的
// mostRight.right不可能为null,只可能是指向cur
System.out.println(cur.val);
mostRight.right = cur;
cur = cur.left;
continue;
} else {
// 这里发现cur的most.right为cur,那么说明这是第二次回到cur
// 并且cur就是通过mostRight.right回来的,所以才会发现mostRight.right为cur
mostRight.right = null;
}
} else {
// 在原有代码的基础上添加这个else分支
// 这个else对应的就是cur.left == null的情况
// 对于先序序列,我们应该将其记录,这里以打印表示记录
System.out.println(cur.val);
}
cur = cur.right;
}
}
3.3 基于Morris遍历加工出中序序列
3.4 基于Morris遍历加工出后序序列
四、Morris遍历代码完整实现
package binarytreevisit;
/**
* @author 西城风雨楼
*/
public class BinaryTree2<V> {
private TreeNode<V> root;
public String preOrderRecur() {
StringBuilder pre = new StringBuilder();
preOrder(root, pre);
return pre.toString();
}
public String inOrderRecur() {
StringBuilder in = new StringBuilder();
inOrder(root, in);
return in.toString();
}
public String postOrderRecur() {
StringBuilder post = new StringBuilder();
postOrder(root, post);
return post.toString();
}
private void preOrder(TreeNode<V> root, StringBuilder pre) {
if (root == null) {
return;
}
pre.append(root.value).append("\t");
preOrder(root.left, pre);
preOrder(root.right, pre);
}
private void inOrder(TreeNode<V> root, StringBuilder in) {
if (root == null) {
return;
}
inOrder(root.left, in);
in.append(root.value).append("\t");
inOrder(root.right, in);
}
private void postOrder(TreeNode<V> root, StringBuilder post) {
if (root == null) {
return;
}
postOrder(root.left, post);
postOrder(root.right, post);
post.append(root.value).append("\t");
}
public String inOrderMorris() {
StringBuilder in = new StringBuilder();
inOrderUseMorris(root, in);
return in.toString();
}
public String postOrderMorris() {
StringBuilder post = new StringBuilder();
postOrderUseMorris(root, post);
return post.toString();
}
public String preOrderMorris() {
StringBuilder pre = new StringBuilder();
preOrderUseMorris(root, pre);
return pre.toString();
}
private void inOrderUseMorris(TreeNode<V> root, StringBuilder in) {
TreeNode<V> cur = root;
while (cur != null) {
if (cur.left != null) {
TreeNode<V> mostRight = cur.left;
while (mostRight.right != null && mostRight.right != cur) {
mostRight = mostRight.right;
}
if (mostRight.right == null) {
mostRight.right = cur;
cur = cur.left;
continue;
} else {
in.append(cur.value).append("\t");
mostRight.right = null;
}
} else {
in.append(cur.value).append("\t");
}
cur = cur.right;
}
}
private void preOrderUseMorris(TreeNode<V> root, StringBuilder pre) {
TreeNode<V> cur = root;
while (cur != null) {
if (cur.left != null) {
TreeNode<V> mostRight = cur.left;
while (mostRight.right != null && mostRight.right != cur) {
mostRight = mostRight.right;
}
if (mostRight.right == null) {
pre.append(cur.value).append("\t");
mostRight.right = cur;
cur = cur.left;
continue;
} else {
mostRight.right = null;
}
} else {
pre.append(cur.value).append("\t");
}
cur = cur.right;
}
}
private void postOrderUseMorris(TreeNode<V> root, StringBuilder post) {
TreeNode<V> cur = root;
while (cur != null) {
if (cur.left != null) {
TreeNode<V> mostRight = cur.left;
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;
// 第二次到达的时候,需要处理左孩子,逆序收集
TreeNode<V> reverse = reverseWithRight(cur.left);
TreeNode<V> visit = reverse;
while (visit != null) {
post.append(visit.value).append("\t");
visit = visit.right;
}
reverseWithRight(reverse);
}
}
cur = cur.right;
}
TreeNode<V> reverse = reverseWithRight(root);
cur = reverse;
while (cur != null) {
post.append(cur.value).append("\t");
cur = cur.right;
}
reverseWithRight(reverse);
}
/**
* 将root的以右孩子进行反转
*
* @param node 结点
* @return 返回反转之后的结点
*/
private TreeNode<V> reverseWithRight(TreeNode<V> node) {
TreeNode<V> cur = node;
TreeNode<V> next;
TreeNode<V> pre = null;
while (cur != null) {
next = cur.right;
cur.right = pre;
pre = cur;
cur = next;
}
return pre;
}
/**
* 根据先序和中序重建二叉树
*
* @param pre 先序
* @param mid 中序
*/
public void rebuild(V[] pre, V[] mid) {
root = rebuildHelper(pre, 0, pre.length - 1,
mid, 0, mid.length - 1);
}
/**
* 根据先序和中序重建二叉树,函数不保证先序和中序序列的合法性
* 以及是否匹配
*
* @param pre 先序
* @param preLeft 先序起始位置
* @param preRight 先序终止位置
* @param mid 中序
* @param midLeft 中序起始位置
* @param midRight 中序终止位置
* @return 返回重建的二叉树的根节点
*/
private TreeNode<V> rebuildHelper(V[] pre,
int preLeft,
int preRight,
V[] mid,
int midLeft,
int midRight) {
if (preLeft > preRight) {
return null;
}
TreeNode<V> root = new TreeNode<>(pre[preLeft]);
if (preLeft == preRight) {
return root;
}
// 找到左右子树的分界位置
int leftBorder = Integer.MIN_VALUE;
for (int i = midLeft; i <= midRight; i++) {
if (root.value.equals(mid[i])) {
leftBorder = i;
break;
}
}
// 这里border可能找不到
if (leftBorder == Integer.MIN_VALUE) {
throw new RuntimeException("序列错误");
}
// 求出左子树的大小
int leftChildSize = leftBorder - midLeft;
// 递归创建左子树
root.left = rebuildHelper(pre, preLeft + 1, preLeft + leftChildSize,
mid, midLeft, leftBorder - 1);
// 递归创建右子树
root.right = rebuildHelper(pre, preLeft + 1 + leftChildSize, preRight,
mid, leftBorder + 1, midRight);
return root;
}
private static class TreeNode<V> {
public V value;
public TreeNode<V> left;
public TreeNode<V> right;
public TreeNode(V value, TreeNode<V> left, TreeNode<V> right) {
this.value = value;
this.left = left;
this.right = right;
}
public TreeNode(V value) {
this.value = value;
}
}
public static void main(String[] args) {
Integer[] pre = new Integer[]{1, 2, 4, 8, 9, 5, 3, 6, 10, 7};
Integer[] in = new Integer[]{8, 4, 9, 2, 5, 1, 10, 6, 3, 7};
BinaryTree2<Integer> tree = new BinaryTree2<>();
tree.rebuild(pre, in);
System.out.println("先序:");
System.out.println(tree.preOrderRecur());
System.out.println(tree.preOrderMorris());
System.out.println("中序:");
System.out.println(tree.inOrderRecur());
System.out.println(tree.inOrderMorris());
System.out.println("后序:");
System.out.println(tree.postOrderRecur());
System.out.println(tree.postOrderMorris());
}
}