二叉树遍历-线索二叉树(Morris)
https://www.bilibili.com/video/BV1Jv411A7Ty?p=31
1、前序遍历-线索二叉树
https://www.bilibili.com/video/BV1Jv411A7Ty?p=32
//1、前序-线索二叉树:根 左 右
public static void morrisPre(TreeNode cur){
if(cur==null){
return;
}
TreeNode mosRight=null;
while(cur!=null){
mosRight=cur.left;
if(mosRight!=null){
while (mosRight.right!=null&&mosRight.right!=cur){
mosRight=mosRight.right;
}
if(mosRight.right==null){
//1、重点:建立线索指针
mosRight.right=cur;
System.out.println(cur.val);
cur=cur.left; //指针左移,并继续找前区节点
continue;
}else{
//2、重点:mosRight.right=cur; 删除线索指针
mosRight.right=null;
}
}else{
System.out.println(cur.val);
}
cur=cur.right;
}
}
2、中序遍历-线索二叉树(常用)
https://www.bilibili.com/video/BV1Jv411A7Ty?p=32
中序遍历是比较容易也是常用的方法,从左向右看,其实中序遍历就是一个二叉树的投影。
左子树最右侧的节点就是右侧节点的前驱节点,比如找 1 的前驱节点就是 7 。
//2、中序-线索二叉树:左 根 右
public static void morrisMid(TreeNode cur){
if(cur==null){
return;
}
TreeNode mosRight=null;
while(cur!=null){
mosRight=cur.left;
if(mosRight!=null){
while (mosRight.right!=null&&mosRight.right!=cur){
mosRight=mosRight.right;
}
if(mosRight.right==null){
//1、重点:建立线索指针
mosRight.right=cur;
cur=cur.left; //指针左移,并继续找前区节点
continue;
}else{
//2、重点:mosRight.right=cur; 删除线索指针
mosRight.right=null;
}
}else{
}
System.out.println(cur.val);
cur=cur.right;
}
}
3、后序遍历-线索二叉树(不推荐)
https://www.bilibili.com/video/BV1Jv411A7Ty?p=33
//3、后序-线索二叉树:左 右 根
// Morris后续遍历:只关注有左子树的节点,第二次遍历到的时候逆序打印左子树的右边界,最后再单独打印整棵树的右边界
public static void morrisPost(TreeNode root){
if(root == null){
return;
}
TreeNode current = root;
TreeNode mostRight = null;
while(current != null){
mostRight = current.left;
// current 有左子树时,就要进行Morris遍历
if(mostRight != null){
while(mostRight.right != null && mostRight.right != current){
// 找到左子树的最右子节点mostRight
mostRight = mostRight.right;
}
// mosrRight找到了,说明是第一次来到cur
if(mostRight.right == null){
mostRight.right = current;
current = current.left;
continue;
}else{
mostRight.right = null;
// 第二次到达该节点,就逆序打印其左子树的右边界
printEdge(current.left);
}
}
// 无左子树 或者左子树已经遍历完了就遍历右子树
current = current.right;
}
// 打印整棵树的右边界
printEdge(root);
}
// 逆序打印node节点的右边界,单链表反转的形式实现逆序
public static void printEdge(TreeNode node){
TreeNode tail = reverseEdge(node);
TreeNode current = tail; // 记录下,用于后面的反转
while(current != null){
System.out.print(current.val + " ");
current = current.right;
}
// 打印完成,还需要将树还原,再反转一次
reverseEdge(tail);
}
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;
}
实验源码:
package com.example.rabbitmq;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
@SpringBootTest
/*
二叉树遍历-Morris线索二叉树
*/
class SuanfaApplicationTests19 {
//定义一个二叉树
static class TreeNode{
public int val;
public TreeNode left;
public TreeNode right;
public int deep;//深度
public TreeNode(){
}
public TreeNode(int val){
this.val=val;}
public TreeNode(int val,TreeNode left,TreeNode right){
this.val=val;
this.left=left;
this.right=right;
}
}
//1、前序-线索二叉树:根 左 右
public static void morrisPre(TreeNode cur){
if(cur==null){
return;
}
TreeNode mosRight=null;
while(cur!=null){
mosRight=cur.left;
if(mosRight!=null){
while (mosRight.right!=null&&mosRight.right!=cur){
mosRight=mosRight.right;
}
if(mosRight.right==null){
//1、重点:建立线索指针
mosRight.right=cur;
System.out.println(cur.val);
cur=cur.left; //指针左移,并继续找前区节点
continue;
}else{
//2、重点:mosRight.right=cur; 删除线索指针
mosRight.right=null;
}
}else{
System.out.println(cur.val);
}
cur=cur.right;
}
}
//2、中序-线索二叉树:左 根 右
public static void morrisMid(TreeNode cur){
if(cur==null){
return;
}
TreeNode mosRight=null;
while(cur!=null){
mosRight=cur.left;
if(mosRight!=null){
while (mosRight.right!=null&&mosRight.right!=cur){
mosRight=mosRight.right;
}
if(mosRight.right==null){
//1、重点:建立线索指针
mosRight.right=cur;
cur=cur.left; //指针左移,并继续找前区节点
continue;
}else{
//2、重点:mosRight.right=cur; 删除线索指针
mosRight.right=null;
}
}else{
}
System.out.println(cur.val);
cur=cur.right;
}
}
//3、后序-线索二叉树:左 右 根
// Morris后续遍历:只关注有左子树的节点,第二次遍历到的时候逆序打印左子树的右边界,最后再单独打印整棵树的右边界
public static void morrisPost(TreeNode root){
if(root == null){
return;
}
TreeNode current = root;
TreeNode mostRight = null;
while(current != null){
mostRight = current.left;
// current 有左子树时,就要进行Morris遍历
if(mostRight != null){
while(mostRight.right != null && mostRight.right != current){
// 找到左子树的最右子节点mostRight
mostRight = mostRight.right;
}
// mosrRight找到了,说明是第一次来到cur
if(mostRight.right == null){
mostRight.right = current;
current = current.left;
continue;
}else{
mostRight.right = null;
// 第二次到达该节点,就逆序打印其左子树的右边界
printEdge(current.left);
}
}
// 无左子树 或者左子树已经遍历完了就遍历右子树
current = current.right;
}
// 打印整棵树的右边界
printEdge(root);
}
// 逆序打印node节点的右边界,单链表反转的形式实现逆序
public static void printEdge(TreeNode node){
TreeNode tail = reverseEdge(node);
TreeNode current = tail; // 记录下,用于后面的反转
while(current != null){
System.out.print(current.val + " ");
current = current.right;
}
// 打印完成,还需要将树还原,再反转一次
reverseEdge(tail);
}
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;
}
@Test
public void sf0(){
//构造一个二叉树
TreeNode node7=new TreeNode(7,null,null);
TreeNode node6=new TreeNode(6,null,null);
TreeNode node5=new TreeNode(5,node6,node7);
TreeNode node4=new TreeNode(4,null,null);
TreeNode node3=new TreeNode(3,null,null);
TreeNode node2=new TreeNode(2,node4,node5);
TreeNode node1=new TreeNode(1,node2,node3);
System.out.println("-------前序遍历-线索二叉树------");
morrisPre(node1);
System.out.println("-------中序遍历-线索二叉树------");
morrisMid(node1);
System.out.println("-------后序遍历-线索二叉树------");
morrisPost(node1);
}
}
结果:
-------前序遍历-线索二叉树------
1
2
4
5
6
7
3
-------中序遍历-线索二叉树------
4
2
6
5
7
1
3
-------后序遍历-线索二叉树------
4
6
7
5
2
3
1