线索化二叉树的创建和遍历(前序、中序、后序)

线索化二叉树的创建和遍历(前序、中序、后序)

须知,本篇博文的每个过程中出现的代码片段都是为了方便讲解而引用的重要代码(作为讲解使用),如果需要源代码可以在最后一项源代码处获取


*什么是线索化二叉树?*

  • 首先,n个结点的二叉链表中含有n+1 公式 2n-(n-1)=n+1 个空指针域。利用二叉链表中的空指针域,存放指向该结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索")
    • 在遍历结果中,一个结点的前一个结点,称为前驱结点
    • 在遍历结果中,一个结点的后一个结点,称为后继结点
  • 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树中序线索二叉树后序线索二叉树三种,即按照二叉树的前、中、后序遍历的结果创建出对应的线索化二叉树

线索化的实现

首先我们先来定义一个节点类,供下面各种线索化使用,如下

package edu.hebeu.cueing;

public class Node {
	
	private String data;
	
	private Node left; // 左节点
	
	private Node right; // 右节点
	
	private Node parent; // 后序线索化的时候需要使用
	
	/**
	 * 标识left指向的类型,left == 0,表示指向的是左子树;left == 1,表示指向的是前驱节点
	 */
	private int leftType;
	
	/**
	 * 标识right指向的类型,right == 0,表示指向的是右子树;right == 1,表示指向的是后继节点
	 */
	private int rightType;
	
	public Node(String data) {
		this.data = data;
	}
	
	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}

	public Node getLeft() {
		return left;
	}

	public void setLeft(Node left) {
		this.left = left;
	}

	public Node getRight() {
		return right;
	}

	public void setRight(Node right) {
		this.right = right;
	}

	public Node getParent() {
		return parent;
	}

	public void setParent(Node parent) {
		this.parent = parent;
	}

	public int getLeftType() {
		return leftType;
	}

	public void setLeftType(int leftType) {
		this.leftType = leftType;
	}

	public int getRightType() {
		return rightType;
	}

	public void setRightType(int rightType) {
		this.rightType = rightType;
	}

	@Override
	public String toString() {
		return "Node [data=" + data + "]";
	}
	
	/**
	 * 前序遍历
	 */
	public void preOrder() {
		// TODO 输出当前节点(初始时就是root根节点)
		System.out.println(this);
		// TODO 如果当前节点的左子节点不为空,则递归继续前序遍历
		if (left != null) {
			left.preOrder();
		}
		// TODO 如果当前节点的右子节点不为空,则递归继续前序遍历
		if (right != null) {
			right.preOrder();
		}
	}
	
	/**
	 * 中序遍历
	 */
	public void infixOrder() {
		// TODO 如果当前节点的左子节点不为空,则递归继续中序遍历
		if (left != null) {
			left.infixOrder();
		}
		// TODO 输出当前节点(初始时就是root根节点)
		System.out.println(this);
		// TODO 如果当前节点的右子节点不为空,则递归继续中序遍历
		if (right != null) {
			right.infixOrder();
		}
	}
	
	/**
	 * 后序遍历
	 */
	public void postOrder() {
		// TODO 如果当前节点的左子节点不为空,则递归继续后序遍历
		if (left != null) {
			left.postOrder();
		}
		// TODO 如果当前节点的右子节点不为空,则递归继续后序遍历
		if (right != null) {
			right.postOrder();
		}
		// TODO 输出当前节点(初始时就是root根节点)
		System.out.println(this);
	}
	
}

图解

前序、中序、后序线索化二叉树的图解如下:
在这里插入图片描述

前序线索化二叉树

总体思路:

  1. 线索化当前节点
  2. 前序线索化左子树
  3. 前序线索化右子树

代码如下:

public class CueingBinaryTree {
	/**
	 * 根节点
	 */
	private Node root;
	
	/**
	 * 为了实现线索化,创建一个的指向当前节点的前驱节点的引用变量,在每次递归进行线索化时该变量
	 * 总是保留前一个节点,初始值为null
	 */
	private Node pre;
	
	/**
	 * 前序线索化的方法(外部直接调用该方法实现二叉树的前序线索化)
	 */
	public void preCueing() {
		preCueing(root);
	}
	
	/**
	 * 对二叉树进行前序线索化的方法,编码步骤:
	 * 	1. 线索化当前节点
	 * 	2. 前序线索化左子树
	 * 	3. 前序线索化右子树
	 * @param node
	 */
	public void preCueing(Node node) {
		// TODO 如果当前需要线索化的节点为空,就不能线索化
		if (node == null) {
			return;
		}
		// TODO 线索化当前节点(处理前驱节点)
		if (node.getLeft() == null) { // 如果当前线索化节点的左指针为空
			node.setLeft(pre); // 将当前节点的左指针指向当前节点的前驱节点
			node.setLeftType(1); // 修改当前节点的左指针类型为1(指向的类型为前驱节点)
		}
		/* TODO 线索化当前节点(处理后继节点),该步实际是下一次递归完成的,
		 * 即:当前节点 就是  下一次递归的pre;当前节点的后继节点 就是 下一次递归的当前节点node
		 */
		if (pre != null &&
				pre.getRight() == null) { // 如果当前线索化节点的右指针为空
			pre.setRight(node); // 将当前节点的右指针指向当前节点的前驱节点
			pre.setRightType(1); // 修改当前节点的右指针类型为1(指向的类型为后驱节点)
		}
		/**每线索化处理一个节点后,让当前节点作为下一个节点的前驱节点*/
		pre = node;
		// TODO 前序线索化左子树
		if (node.getLeftType() != 1) {
			preCueing(node.getLeft());
		}
		// TODO 前序线索化右子树
		if (node.getRightType() != 1) {
			preCueing(node.getRight());
		}
	}
}

测试:

public class Test {
	
	public static void main(String[] args) {
		testPreCueing();
		
	}
	
	public static void testPreCueing() {
		Node node0 = new Node("data0");
		Node node1 = new Node("data1");
		Node node2 = new Node("data2");
		Node node3 = new Node("data3");
		Node node4 = new Node("data4");
		Node node5 = new Node("data5");
		
		// 创建二叉树
		node0.setLeft(node1);
		node0.setRight(node2);
		node1.setLeft(node3);
		node1.setRight(node4);
		node2.setLeft(node5);
		CueingBinaryTree cueingBinaryTree = new CueingBinaryTree();
		cueingBinaryTree.setRoot(node0);
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
		// 进行前序线索化
		cueingBinaryTree.preCueing();
		System.out.println("-----------------------前序线索化后---------------------------");
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
	}
}

在这里插入图片描述

中序线索化二叉树

基本思路:

  1. 中序线索化左子树
  2. 线索化当前节点
  3. 中序线索化右子树

代码如下:

public class CueingBinaryTree {
	
	/**
	 * 根节点
	 */
	private Node root;
	
	/**
	 * 为了实现线索化,创建一个的指向当前节点的前驱节点的引用变量,在每次递归进行线索化时该变量
	 * 总是保留前一个节点,初始值为null
	 */
	private Node pre;

	/**
	 * 中序线索化的方法(外部直接调用该方法实现二叉树的中序线索化)
	 */
	public void infixCueing() {
		infixCueing(root);
	}
	
	/**
	 * 对二叉树进行中序线索化的方法,编码步骤:
	 * 	1. 中序线索化左子树
	 * 	2. 线索化当前节点
	 * 	3. 中序线索化右子树
	 * @param node 当前需要线索化的节点
	 */
	public void infixCueing(Node node) {
		// 如果当前需要线索化的节点为空,就不能线索化
		if (node == null) {
			return;
		}
		// TODO 中线索化左子树
		infixCueing(node.getLeft());
		// TODO 线索化当前节点(处理前驱节点)
		if (node.getLeft() == null) { // 如果当前线索化节点的左指针为空
			node.setLeft(pre); // 将当前节点的左指针指向当前节点的前驱节点
			node.setLeftType(1); // 修改当前节点的左指针类型为1(指向的类型为前驱节点)
		}
		/* TODO 线索化当前节点(处理后继节点),该步实际是下一次递归完成的,
		 * 即:当前节点 就是  下一次递归的pre;当前节点的后继节点 就是 下一次递归的当前节点node
		 */
		if (pre != null &&
				pre.getRight() == null) { // 如果当前线索化节点的右指针为空
			pre.setRight(node); // 将当前节点的右指针指向当前节点的前驱节点
			pre.setRightType(1); // 修改当前节点的右指针类型为1(指向的类型为后驱节点)
		}
		/**每线索化处理一个节点后,让当前节点作为下一个节点的前驱节点*/
		pre = node;
		// TODO 中序线索化右子树
		infixCueing(node.getRight());
	}
}

测试:

public class Test {
	
	public static void main(String[] args) {
		testInfixCueing();
	}
	
	public static void testInfixCueing() {
		Node node0 = new Node("data0");
		Node node1 = new Node("data1");
		Node node2 = new Node("data2");
		Node node3 = new Node("data3");
		Node node4 = new Node("data4");
		Node node5 = new Node("data5");
		
		// 创建二叉树
		node0.setLeft(node1);
		node0.setRight(node2);
		node1.setLeft(node3);
		node1.setRight(node4);
		node2.setLeft(node5);
		CueingBinaryTree cueingBinaryTree = new CueingBinaryTree();
		cueingBinaryTree.setRoot(node0);
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
		// 进行中序线索化
		cueingBinaryTree.infixCueing();
		System.out.println("-----------------------中序线索化后---------------------------");
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
	}
}

在这里插入图片描述

后序线索化二叉树

基本思路:

  1. 后序线索化左子树
  2. 后序线索化右子树
  3. 线索化当前节点

代码如下:

public class CueingBinaryTree {
	
	/**
	 * 根节点
	 */
	private Node root;
	
	/**
	 * 为了实现线索化,创建一个的指向当前节点的前驱节点的引用变量,在每次递归进行线索化时该变量
	 * 总是保留前一个节点,初始值为null
	 */
	private Node pre;
	
	/**
	 * 后序线索化的方法(外部直接调用该方法实现二叉树的后序线索化)
	 */
	public void postCueing() {
		postCueing(root);
	}
	
	/**
	 * 对二叉树进行后序线索化的方法,编码步骤:
	 * 	1. 后序线索化左子树
	 * 	2. 后序线索化右子树
	 * 	3. 线索化当前节点
	 * @param node 待线索化的节点
	 */
	public void postCueing(Node node) {
		// 如果当前需要线索化的节点为空,就不能线索化
		if (node == null) {
			return;
		}
		// TODO 线索化左子树
		postCueing(node.getLeft());
		// TODO 线索化右子树
		postCueing(node.getRight());
		// TODO 线索化当前节点(前驱节点)
		if (node.getLeft() == null) { // 如果当前线索化节点的左指针为空
			node.setLeft(pre); // 将当前节点的左指针指向当前节点的前驱节点
			node.setLeftType(1); // 修改当前节点的左指针类型为1(指向的类型为前驱节点)
		}
		/* TODO 线索化当前节点(处理后继节点),该步实际是下一次递归完成的,
		 * 即:当前节点 就是  下一次递归的pre;当前节点的后继节点 就是 下一次递归的当前节点node
		 */
		if (pre != null && 
				pre.getRight() == null) { // 如果当前线索化节点的右指针为空
			pre.setRight(node); // 将当前节点的右指针指向当前节点的前驱节点
			pre.setRightType(1); // 修改当前节点的右指针类型为1(指向的类型为后驱节点)
		}
		/**每线索化处理一个节点后,让当前节点作为下一个节点的前驱节点*/
		pre = node;
	}
}

测试:

public class Test {
	
	public static void main(String[] args) {
		testPostCueing();
	}
	
	public static void testPostCueing() {
		Node node0 = new Node("data0");
		Node node1 = new Node("data1");
		Node node2 = new Node("data2");
		Node node3 = new Node("data3");
		Node node4 = new Node("data4");
		Node node5 = new Node("data5");
		
		// 创建二叉树
		node0.setLeft(node1);
		node0.setRight(node2);
		node1.setLeft(node3);
		node1.setRight(node4);
		node1.setParent(node0);
		node2.setLeft(node5);
		node2.setParent(node0);
		node3.setParent(node1);
		node4.setParent(node1);
		node5.setParent(node2);
		CueingBinaryTree cueingBinaryTree = new CueingBinaryTree();
		cueingBinaryTree.setRoot(node0);
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
		// 进行中序线索化
		cueingBinaryTree.postCueing();
		System.out.println("-----------------------后序线索化后---------------------------");
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
	}

}

在这里插入图片描述

遍历线索化二叉树的实现

因为对二叉树线索化后,以往的前序、中序、后序就不能继续使用了(叶子节点不能通过判空来决定怎样循环,导致出现栈溢出错误,所以我们要对每种线索化后的二叉树制定特殊的遍历方式)

遍历前序线索化二叉树

重要代码

public class CueingBinaryTree {
	
	/**
	 * 根节点
	 */
	private Node root;
	
	/**
	 * 为了实现线索化,创建一个的指向当前节点的前驱节点的引用变量,在每次递归进行线索化时该变量
	 * 总是保留前一个节点,初始值为null
	 */
	private Node pre;

	/**
	 * 遍历前序线索化二叉树的方法
	 */
	public void orderPreCueing() {
		Node node = root; // 该变量用来保存当前遍历的节点,默认为root
		while(node != null) {
            while(node.getLeftType() != 1) {
                System.out.println(node.getData());
                node = node.getLeft();
            }
            // TODO 打印当前节点
            System.out.println(node.getData());
            /* 程序执行到此,说明当前节点已经不是后继节点了,那么此时我们应该将当前节点变成当前节点的右子节点,
			 * 在下次循环时通过该节点再找到线索化后的节点,以进行遍历...
			 */
            node = node.getRight();
        }
	}

}

对上面的前序线索二叉树遍历测试

public class Test {
	
	public static void main(String[] args) {
		testPreCueing();
	}
	
	public static void testPreCueing() {
		Node node0 = new Node("data0");
		Node node1 = new Node("data1");
		Node node2 = new Node("data2");
		Node node3 = new Node("data3");
		Node node4 = new Node("data4");
		Node node5 = new Node("data5");
		
		// 创建二叉树
		node0.setLeft(node1);
		node0.setRight(node2);
		node1.setLeft(node3);
		node1.setRight(node4);
		node2.setLeft(node5);
		CueingBinaryTree cueingBinaryTree = new CueingBinaryTree();
		cueingBinaryTree.setRoot(node0);
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
		// 进行前序线索化
		cueingBinaryTree.preCueing();
		System.out.println("-----------------------前序线索化后---------------------------");
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
		System.out.println("---------------遍历-------------");
		cueingBinaryTree.orderPreCueing();
		
	}

}

在这里插入图片描述

遍历中序线索化二叉树

重要代码

public class CueingBinaryTree {
	
	/**
	 * 根节点
	 */
	private Node root;
	
	/**
	 * 为了实现线索化,创建一个的指向当前节点的前驱节点的引用变量,在每次递归进行线索化时该变量
	 * 总是保留前一个节点,初始值为null
	 */
	private Node pre;

	/**
	 * 遍历中序线索化二叉树的方法
	 */
	public void orderInfixCueing() {
		Node node = root; // 该变量用来存储当前遍历的节点,默认为root
		// TODO 开始遍历
		while (node != null) {
			// TODO 找到leftType == 1的节点,因为leftType == 1,表示该节点是按照线索化处理后的有效节点
			while (node.getLeftType() != 1) {
				node = node.getLeft(); // 获取当前节点的左子节点
			}
			// TODO 打印当前节点
			System.out.println(node);
			// TODO 如果当前节点的右节点指向的是后继节点,就一直获取并打印其后继节点
			while (node.getRightType() == 1) {
				node = node.getRight(); // 获取到当前节点的后继节点
				System.out.println(node);
			}
			/* 程序执行到此,说明当前节点已经不是后继节点了,那么此时我们应该将当前节点变成当前节点的右子节点,
			 * 在下次循环时通过该节点再找到线索化后的节点,以进行遍历...
			 */
			node = node.getRight();
		}
	}

}

对上面的中序线索二叉树遍历测试

public class Test {
	
	public static void main(String[] args) {
		testInfixCueing();
	}
	
	public static void testInfixCueing() {
		Node node0 = new Node("data0");
		Node node1 = new Node("data1");
		Node node2 = new Node("data2");
		Node node3 = new Node("data3");
		Node node4 = new Node("data4");
		Node node5 = new Node("data5");
		
		// 创建二叉树
		node0.setLeft(node1);
		node0.setRight(node2);
		node1.setLeft(node3);
		node1.setRight(node4);
		node2.setLeft(node5);
		CueingBinaryTree cueingBinaryTree = new CueingBinaryTree();
		cueingBinaryTree.setRoot(node0);
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
		// 进行中序线索化
		cueingBinaryTree.infixCueing();
		System.out.println("-----------------------中序线索化后---------------------------");
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
		System.out.println("---------------遍历-------------");
		cueingBinaryTree.orderInfixCueing();
		
	}
}

在这里插入图片描述

遍历后序线索化二叉树

重要代码

public class CueingBinaryTree {
	
	/**
	 * 根节点
	 */
	private Node root;
	
	/**
	 * 为了实现线索化,创建一个的指向当前节点的前驱节点的引用变量,在每次递归进行线索化时该变量
	 * 总是保留前一个节点,初始值为null
	 */
	private Node pre;
	
	/**
	 * 遍历后序线索化二叉树的方法
	 */
	public void orderPostCueing() {
		 //1、找后序遍历方式开始的节点
        Node node = root;

        while(node != null && node.getLeftType() != 1) {
            node = node.getLeft();
        }
        
        while(node != null) {
            //右节点是线索
            if(node.getRightType() == 1) {
                System.out.println(node);
                pre = node;
                node = node.getRight();
            } else {
                //如果上个处理的节点是当前节点的右节点
                if(node.getRight() == pre) {
                	
                    System.out.println(node);
                    if(node == root) {
                        return;
                    }
                    pre = node;
                    node = node.getParent();

                } else {    //如果从左节点的进入则找到有子树的最左节点
                    node = node.getRight();
                    while(node != null && node.getLeftType() != 1) {
                        node = node.getLeft();
                    }
                }
            }
        }
	}
	
}

对上面的后序线索二叉树遍历测试

public class Test {
	
	public static void main(String[] args) {
		testPostCueing();
	}

	public static void testPostCueing() {
		Node node0 = new Node("data0");
		Node node1 = new Node("data1");
		Node node2 = new Node("data2");
		Node node3 = new Node("data3");
		Node node4 = new Node("data4");
		Node node5 = new Node("data5");
		
		// 创建二叉树
		node0.setLeft(node1);
		node0.setRight(node2);
		node1.setLeft(node3);
		node1.setRight(node4);
		node1.setParent(node0);
		node2.setLeft(node5);
		node2.setParent(node0);
		node3.setParent(node1);
		node4.setParent(node1);
		node5.setParent(node2);
		CueingBinaryTree cueingBinaryTree = new CueingBinaryTree();
		cueingBinaryTree.setRoot(node0);
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
		// 进行中序线索化
		cueingBinaryTree.postCueing();
		System.out.println("-----------------------后序线索化后---------------------------");
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
		System.out.println("---------------遍历-------------");
		cueingBinaryTree.orderPostCueing();
	}

}

在这里插入图片描述

源代码

Node类

package edu.hebeu.cueing;

public class Node {
	
	private String data;
	
	private Node left; // 左节点
	
	private Node right; // 右节点
	
	private Node parent; // 后序线索化的时候需要使用
	
	/**
	 * 标识left指向的类型,left == 0,表示指向的是左子树;left == 1,表示指向的是前驱节点
	 */
	private int leftType;
	
	/**
	 * 标识right指向的类型,right == 0,表示指向的是右子树;right == 1,表示指向的是后继节点
	 */
	private int rightType;
	
	public Node(String data) {
		this.data = data;
	}
	
	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}

	public Node getLeft() {
		return left;
	}

	public void setLeft(Node left) {
		this.left = left;
	}

	public Node getRight() {
		return right;
	}

	public void setRight(Node right) {
		this.right = right;
	}

	public Node getParent() {
		return parent;
	}

	public void setParent(Node parent) {
		this.parent = parent;
	}

	public int getLeftType() {
		return leftType;
	}

	public void setLeftType(int leftType) {
		this.leftType = leftType;
	}

	public int getRightType() {
		return rightType;
	}

	public void setRightType(int rightType) {
		this.rightType = rightType;
	}

	@Override
	public String toString() {
		return "Node [data=" + data + "]";
	}
	
	/**
	 * 前序遍历
	 */
	public void preOrder() {
		// TODO 输出当前节点(初始时就是root根节点)
		System.out.println(this);
		// TODO 如果当前节点的左子节点不为空,则递归继续前序遍历
		if (left != null) {
			left.preOrder();
		}
		// TODO 如果当前节点的右子节点不为空,则递归继续前序遍历
		if (right != null) {
			right.preOrder();
		}
	}
	
	/**
	 * 中序遍历
	 */
	public void infixOrder() {
		// TODO 如果当前节点的左子节点不为空,则递归继续中序遍历
		if (left != null) {
			left.infixOrder();
		}
		// TODO 输出当前节点(初始时就是root根节点)
		System.out.println(this);
		// TODO 如果当前节点的右子节点不为空,则递归继续中序遍历
		if (right != null) {
			right.infixOrder();
		}
	}
	
	/**
	 * 后序遍历
	 */
	public void postOrder() {
		// TODO 如果当前节点的左子节点不为空,则递归继续后序遍历
		if (left != null) {
			left.postOrder();
		}
		// TODO 如果当前节点的右子节点不为空,则递归继续后序遍历
		if (right != null) {
			right.postOrder();
		}
		// TODO 输出当前节点(初始时就是root根节点)
		System.out.println(this);
	}
	
}

CueingBinaryTree类

package edu.hebeu.cueing;

/**
 * 线索化二叉树
 * 
 * @author 13651
 *
 */
public class CueingBinaryTree {
	
	/**
	 * 根节点
	 */
	private Node root;
	
	/**
	 * 为了实现线索化,创建一个的指向当前节点的前驱节点的引用变量,在每次递归进行线索化时该变量
	 * 总是保留前一个节点,初始值为null
	 */
	private Node pre;
	
	/**
	 * 线索化的方式
	 * @author 13651
	 *
	 */
	public static enum CUEING_TYPE {
		PRE_CUEING, INFIX_CUEING, POST_CUEING
	}
	
	public CueingBinaryTree() {
	}
	
	/**
	 * 直接创建指定类型的线索化二叉树
	 * @param type 线索化的类型
	 * @param root 二叉树的根节点
	 */
	public CueingBinaryTree(CUEING_TYPE type, Node root) {
		this.root = root;
		switch (type) {
			case PRE_CUEING:
				 break;
			case INFIX_CUEING: 
				infixCueing();
				break;
			case POST_CUEING: 
				break;
			default:
				break;
		}
	}

	/**
	 * 前序线索化的方法(外部直接调用该方法实现二叉树的前序线索化)
	 */
	public void preCueing() {
		preCueing(root);
	}

	/**
	 * 中序线索化的方法(外部直接调用该方法实现二叉树的中序线索化)
	 */
	public void infixCueing() {
		infixCueing(root);
	}
	
	/**
	 * 后序线索化的方法(外部直接调用该方法实现二叉树的后序线索化)
	 */
	public void postCueing() {
		postCueing(root);
	}
	
	/**
	 * 对二叉树进行前序线索化的方法,编码步骤:
	 * 	1. 线索化当前节点
	 * 	2. 前序线索化左子树
	 * 	3. 前序线索化右子树
	 * @param node
	 */
	public void preCueing(Node node) {
		// TODO 如果当前需要线索化的节点为空,就不能线索化
		if (node == null) {
			return;
		}
		// TODO 线索化当前节点(处理前驱节点)
		if (node.getLeft() == null) { // 如果当前线索化节点的左指针为空
			node.setLeft(pre); // 将当前节点的左指针指向当前节点的前驱节点
			node.setLeftType(1); // 修改当前节点的左指针类型为1(指向的类型为前驱节点)
		}
		/* TODO 线索化当前节点(处理后继节点),该步实际是下一次递归完成的,
		 * 即:当前节点 就是  下一次递归的pre;当前节点的后继节点 就是 下一次递归的当前节点node
		 */
		if (pre != null &&
				pre.getRight() == null) { // 如果当前线索化节点的右指针为空
			pre.setRight(node); // 将当前节点的右指针指向当前节点的前驱节点
			pre.setRightType(1); // 修改当前节点的右指针类型为1(指向的类型为后驱节点)
		}
		/**每线索化处理一个节点后,让当前节点作为下一个节点的前驱节点*/
		pre = node;
		// TODO 前序线索化左子树
		if (node.getLeftType() != 1) {
			preCueing(node.getLeft());
		}
		// TODO 前序线索化右子树
		if (node.getRightType() != 1) {
			preCueing(node.getRight());
		}
	}
	
	/**
	 * 对二叉树进行中序线索化的方法,编码步骤:
	 * 	1. 中序线索化左子树
	 * 	2. 线索化当前节点
	 * 	3. 中序线索化右子树
	 * @param node 当前需要线索化的节点
	 */
	public void infixCueing(Node node) {
		// 如果当前需要线索化的节点为空,就不能线索化
		if (node == null) {
			return;
		}
		// TODO 中线索化左子树
		infixCueing(node.getLeft());
		// TODO 线索化当前节点(处理前驱节点)
		if (node.getLeft() == null) { // 如果当前线索化节点的左指针为空
			node.setLeft(pre); // 将当前节点的左指针指向当前节点的前驱节点
			node.setLeftType(1); // 修改当前节点的左指针类型为1(指向的类型为前驱节点)
		}
		/* TODO 线索化当前节点(处理后继节点),该步实际是下一次递归完成的,
		 * 即:当前节点 就是  下一次递归的pre;当前节点的后继节点 就是 下一次递归的当前节点node
		 */
		if (pre != null &&
				pre.getRight() == null) { // 如果当前线索化节点的右指针为空
			pre.setRight(node); // 将当前节点的右指针指向当前节点的前驱节点
			pre.setRightType(1); // 修改当前节点的右指针类型为1(指向的类型为后驱节点)
		}
		/**每线索化处理一个节点后,让当前节点作为下一个节点的前驱节点*/
		pre = node;
		// TODO 中序线索化右子树
		infixCueing(node.getRight());
	}
	
	/**
	 * 对二叉树进行后序线索化的方法,编码步骤:
	 * 	1. 后序线索化左子树
	 * 	2. 后序线索化右子树
	 * 	3. 线索化当前节点
	 * @param node 待线索化的节点
	 */
	public void postCueing(Node node) {
		// 如果当前需要线索化的节点为空,就不能线索化
		if (node == null) {
			return;
		}
		// TODO 线索化左子树
		postCueing(node.getLeft());
		// TODO 线索化右子树
		postCueing(node.getRight());
		// TODO 线索化当前节点(前驱节点)
		if (node.getLeft() == null) { // 如果当前线索化节点的左指针为空
			node.setLeft(pre); // 将当前节点的左指针指向当前节点的前驱节点
			node.setLeftType(1); // 修改当前节点的左指针类型为1(指向的类型为前驱节点)
		}
		/* TODO 线索化当前节点(处理后继节点),该步实际是下一次递归完成的,
		 * 即:当前节点 就是  下一次递归的pre;当前节点的后继节点 就是 下一次递归的当前节点node
		 */
		if (pre != null && 
				pre.getRight() == null) { // 如果当前线索化节点的右指针为空
			pre.setRight(node); // 将当前节点的右指针指向当前节点的前驱节点
			pre.setRightType(1); // 修改当前节点的右指针类型为1(指向的类型为后驱节点)
		}
		/**每线索化处理一个节点后,让当前节点作为下一个节点的前驱节点*/
		pre = node;
	}
	
	/**
	 * 遍历前序线索化二叉树的方法
	 */
	public void orderPreCueing() {
		Node node = root; // 该变量用来保存当前遍历的节点,默认为root
//		while (node != null) {
//			// TODO 打印当前节点
//			System.out.println(node);
//			// 
//			while (node.getLeftType() != 1) {
//				node = node.getLeft();
//				System.out.println(node);
//			}
//			while (node.getRightType() == 1) {
//				node = node.getRight(); // 获取后继节点
//				System.out.println(node);
//			}
//			/* 程序执行到此,说明当前节点已经不是后继节点了,那么此时我们应该将当前节点变成当前节点的右子节点,
//			 * 在下次循环时通过该节点再找到线索化后的节点,以进行遍历...
//			 */
//			node = node.getRight();
//		}
		while(node != null) {
            while(node.getLeftType() != 1) {
                System.out.println(node.getData());
                node = node.getLeft();
            }
            // TODO 打印当前节点
            System.out.println(node.getData());
            /* 程序执行到此,说明当前节点已经不是后继节点了,那么此时我们应该将当前节点变成当前节点的右子节点,
			 * 在下次循环时通过该节点再找到线索化后的节点,以进行遍历...
			 */
            node = node.getRight();
        }

	}
	
	/**
	 * 遍历中序线索化二叉树的方法
	 */
	public void orderInfixCueing() {
		Node node = root; // 该变量用来存储当前遍历的节点,默认为root
		// TODO 开始遍历
		while (node != null) {
			// TODO 找到leftType == 1的节点,因为leftType == 1,表示该节点是按照线索化处理后的有效节点
			while (node.getLeftType() != 1) {
				node = node.getLeft(); // 获取当前节点的左子节点
			}
			// TODO 打印当前节点
			System.out.println(node);
			// TODO 如果当前节点的右节点指向的是后继节点,就一直获取并打印其后继节点
			while (node.getRightType() == 1) {
				node = node.getRight(); // 获取到当前节点的后继节点
				System.out.println(node);
			}
			/* 程序执行到此,说明当前节点已经不是后继节点了,那么此时我们应该将当前节点变成当前节点的右子节点,
			 * 在下次循环时通过该节点再找到线索化后的节点,以进行遍历...
			 */
			node = node.getRight();
		}
	}
	
	/**
	 * 遍历后序线索化二叉树的方法
	 */
	public void orderPostCueing() {
		 //1、找后序遍历方式开始的节点
        Node node = root;

        while(node != null && node.getLeftType() != 1) {
            node = node.getLeft();
        }
        
        while(node != null) {
            //右节点是线索
            if(node.getRightType() == 1) {
                System.out.println(node);
                pre = node;
                node = node.getRight();
            } else {
                //如果上个处理的节点是当前节点的右节点
                if(node.getRight() == pre) {
                	
                    System.out.println(node);
                    if(node == root) {
                        return;
                    }
                    pre = node;
                    node = node.getParent();

                } else {    //如果从左节点的进入则找到有子树的最左节点
                    node = node.getRight();
                    while(node != null && node.getLeftType() != 1) {
                        node = node.getLeft();
                    }
                }
            }
        }
	}

	/**
	 * 前序遍历,在二叉树线索化以后,传统的前序,中序、后序遍历就不能使用了(每个节点的left和right都不为空,
	 * 导致不能结束递归而出现栈溢出错误)
	 */
	@Deprecated
	public void preOrder() {
		if (root != null) {
			root.preOrder();
		} else {
			System.err.println("二叉树为空!");
		}
	}

	/**
	 * 中序遍历,在二叉树线索化以后,传统的前序,中序、后序遍历就不能使用了(每个节点的left和right都不为空,
	 * 导致不能结束递归而出现栈溢出错误)
	 */
	@Deprecated
	public void infixOrder() {
		if (root != null) {
			root.infixOrder();
		} else {
			System.err.println("二叉树为空!");
		}
	}

	/**
	 * 后序遍历,在二叉树线索化以后,传统的前序,中序、后序遍历就不能使用了(每个节点的left和right都不为空,
	 * 导致不能结束递归而出现栈溢出错误)
	 */
	@Deprecated
	public void postOrder() {
		if (root != null) {
			root.postOrder();
		} else {
			System.err.println("二叉树为空!");
		}
	}

	public Node getRoot() {
		return root;
	}

	public void setRoot(Node root) {
		this.root = root;
	}

}

Test测试类

package edu.hebeu.cueing;

import edu.hebeu.cueing.CueingBinaryTree.CUEING_TYPE;

public class Test {
	
	public static void main(String[] args) {
//		testPreCueing();
//		testInfixCueing();
		testPostCueing();
		
	}
	
	public static void testPreCueing() {
		Node node0 = new Node("data0");
		Node node1 = new Node("data1");
		Node node2 = new Node("data2");
		Node node3 = new Node("data3");
		Node node4 = new Node("data4");
		Node node5 = new Node("data5");
//		Node node6 = new Node("data6");
//		Node node7 = new Node("data7");
//		Node node8 = new Node("data8");
//		Node node9 = new Node("data9");
//		Node node10 = new Node("data10");
//		Node node11 = new Node("data11");
//		Node node12 = new Node("data12");
//		Node node13 = new Node("data13");
//		Node node14 = new Node("data14");
		
		// 创建二叉树
		node0.setLeft(node1);
		node0.setRight(node2);
		node1.setLeft(node3);
		node1.setRight(node4);
		node2.setLeft(node5);
//		node2.setRight(node6);
//		node3.setLeft(node7);
//		node3.setRight(node8);
//		node4.setLeft(node9);
//		node4.setRight(node10);
//		node5.setLeft(node11);
//		node5.setRight(node12);
//		node6.setLeft(node13);
//		node6.setRight(node14);
		CueingBinaryTree cueingBinaryTree = new CueingBinaryTree();
		cueingBinaryTree.setRoot(node0);
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
//		System.out.println("---------------遍历-------------");
//		cueingBinaryTree.preOrder();
		
		// 进行前序线索化
		cueingBinaryTree.preCueing();
		System.out.println("-----------------------前序线索化后---------------------------");
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
		System.out.println("---------------遍历-------------");
		cueingBinaryTree.orderPreCueing();
//		
//		System.out.println(); System.out.println();
		
	}
	
	public static void testInfixCueing() {
		Node node0 = new Node("data0");
		Node node1 = new Node("data1");
		Node node2 = new Node("data2");
		Node node3 = new Node("data3");
		Node node4 = new Node("data4");
		Node node5 = new Node("data5");
//		Node node6 = new Node("data6");
//		Node node7 = new Node("data7");
//		Node node8 = new Node("data8");
//		Node node9 = new Node("data9");
//		Node node10 = new Node("data10");
//		Node node11 = new Node("data11");
//		Node node12 = new Node("data12");
//		Node node13 = new Node("data13");
//		Node node14 = new Node("data14");
		
		// 创建二叉树
		node0.setLeft(node1);
		node0.setRight(node2);
		node1.setLeft(node3);
		node1.setRight(node4);
		node2.setLeft(node5);
//		node2.setRight(node6);
//		node3.setLeft(node7);
//		node3.setRight(node8);
//		node4.setLeft(node9);
//		node4.setRight(node10);
//		node5.setLeft(node11);
//		node5.setRight(node12);
//		node6.setLeft(node13);
//		node6.setRight(node14);
		CueingBinaryTree cueingBinaryTree = new CueingBinaryTree();
		cueingBinaryTree.setRoot(node0);
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
//		System.out.println("---------------遍历-------------");
//		cueingBinaryTree.infixOrder();
		
		// 进行中序线索化
		cueingBinaryTree.infixCueing();
		System.out.println("-----------------------中序线索化后---------------------------");
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
		System.out.println("---------------遍历-------------");
		cueingBinaryTree.orderInfixCueing();
//		
//		System.out.println(); System.out.println();
		
	}
	
	public static void testPostCueing() {
		Node node0 = new Node("data0");
		Node node1 = new Node("data1");
		Node node2 = new Node("data2");
		Node node3 = new Node("data3");
		Node node4 = new Node("data4");
		Node node5 = new Node("data5");
//		Node node6 = new Node("data6");
//		Node node7 = new Node("data7");
//		Node node8 = new Node("data8");
//		Node node9 = new Node("data9");
//		Node node10 = new Node("data10");
//		Node node11 = new Node("data11");
//		Node node12 = new Node("data12");
//		Node node13 = new Node("data13");
//		Node node14 = new Node("data14");
		
		// 创建二叉树
		node0.setLeft(node1);
		node0.setRight(node2);
		node1.setLeft(node3);
		node1.setRight(node4);
		node1.setParent(node0);
		node2.setLeft(node5);
//		node2.setRight(node6);
		node2.setParent(node0);
//		node3.setLeft(node7);
//		node3.setRight(node8);
		node3.setParent(node1);
//		node4.setLeft(node9);
//		node4.setRight(node10);
		node4.setParent(node1);
//		node5.setLeft(node11);
//		node5.setRight(node12);
		node5.setParent(node2);
//		node6.setLeft(node13);
//		node6.setRight(node14);
//		node6.setParent(node2);
//		node7.setParent(node3);
//		node8.setParent(node3);
//		node9.setParent(node4);
//		node10.setParent(node4);
//		node11.setParent(node5);
//		node12.setParent(node5);
//		node13.setParent(node6);
//		node14.setParent(node6);
		CueingBinaryTree cueingBinaryTree = new CueingBinaryTree();
		cueingBinaryTree.setRoot(node0);
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
//		System.out.println("---------------遍历-------------");
//		cueingBinaryTree.postOrder();
		
		// 进行中序线索化
		cueingBinaryTree.postCueing();
		System.out.println("-----------------------后序线索化后---------------------------");
		
		System.out.println("node2的left:" + node2.getLeft() + "; node2的right:" + node2.getRight());
		System.out.println("node3的left:" + node3.getLeft() + "; node3的right:" + node3.getRight());
		System.out.println("node4的left:" + node4.getLeft() + "; node4的right:" + node4.getRight());
		System.out.println("node5的left:" + node5.getLeft() + "; node5的right:" + node5.getRight());
		
		System.out.println("---------------遍历-------------");
		cueingBinaryTree.orderPostCueing();
//		
//		System.out.println(); System.out.println();
		
	}
	
}

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值