线索化指的是:一般二叉树的叶子结点的左右子结点都指向空,但是线索化后,叶子结点的左子结点指向的是“前驱结点”,右子结点指向的是“后继结点”
三种线索化,直接上图、不多说
代码如下,后序线索遍历本人没有写出来
1.结点
class Node{
private int no;
private String name;
private Node left;
private Node right;
/**
* leftType=0 表示left指向的是左子结点 =1指向的是前驱结点
* rightType=0 表示left指向的是右子结点 =1指向的是后继结点
*/
private int leftType;
private int rightType;
public Node(int no, String name) {
this.no = no;
this.name = name;
}
/**
* 前序遍历
*/
public void preVisit(){
System.out.println(this);
if (this.left!=null){
this.left.preVisit();
}
if (this.right!=null){
this.right.preVisit();
}
}
/**
* 中序遍历
*/
public void midVisit(){
if (this.left!=null){
this.left.midVisit();
}
System.out.println(this);
if (this.right!=null){
this.right.midVisit();
}
}
/**
* 后序遍历
*/
public void afterVisit(){
if (this.left!=null){
this.left.afterVisit();
}
if (this.right!=null){
this.right.afterVisit();
}
System.out.println(this);
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
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 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{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
2.线索化二叉树
class ThreadedBinaryTree{
private Node root;
/**
* 在进行递归线索化时,pre指向的是当前结点的前驱结点
*/
private Node pre=null;
public void setRoot(Node root) {
this.root = root;
}
public Node getRoot() {
return root;
}
/**
* 重载下面的前序线索化
*/
public void preThreadedNodes(){
this.preThreadedNodes(this.root);
}
/**
* 重载下面的中序线索化
*/
public void midThreadedNodes(){
this.midThreadedNodes(this.root);
}
/**
* 重载下面的后序线索化
*/
public void afterThreadedNodes(){
this.afterThreadedNodes(this.root);
}
/**
* 中序线索化二叉树
* @param node 当前结点
*/
public void midThreadedNodes(Node node){
if (node==null){
return;
}
//线索化左子树
midThreadedNodes(node.getLeft());
//线索化当前结点
//node.getLeft()==null当前结点的子结点为空 代表当前结点为叶子结点
//所以将node的left指向前驱结点pre(线索化)
if (node.getLeft()==null){
node.setLeft(pre);
//1代表node的left指向的是前驱结点
node.setLeftType(1);
}
//如果前驱结点pre的right结点为空 说明pre是一个叶子结点 上一步还没有对right线索化
//所以在此刻对未线索化的pre的right进行线索化
if (pre!=null&&pre.getRight()==null){
pre.setRight(node);
//1代表npre的right指向的是后继结点
pre.setRightType(1);
}
//移动pre 使之指向当前结点 下一次递归时,pre就成为了下一次递归node的前驱结点
pre=node;
//线索化右子树
midThreadedNodes(node.getRight());
}
/**
* 前序线索化二叉树
* @param node 当前结点
*/
public void preThreadedNodes(Node node){
if(node==null){
return;
}
if(node.getLeft()==null){
node.setLeftType(1);
node.setLeft(pre);
}
/**
* 如果pre的left结点为node(当前) 则不能把pre的right结点设为node(当前) 否则下面的“preThreadedNodes(node.getLeft())”会出现死循环
*/
if(pre!=null&&pre.getRight()==null&&pre.getLeft()!=node){
pre.setRightType(1);
pre.setRight(node);
}
pre=node;
//线索化左子树
/**
* node.getLeftType()==0是为了避免出现死循环
* 因为若当前结点是左叶子结点 在上面的node.setLeft(pre);已经把node的left设为了pre 而node是pre的左子结点
* 那么preThreadedNodes(node.getLeft())又会进入同一个结点 这样无限循环
*/
if (node.getLeftType()==0){
preThreadedNodes(node.getLeft());
}
//线索化右子树
preThreadedNodes(node.getRight());
}
/**
* 后序线索化二叉树
* @param node
*/
public void afterThreadedNodes(Node node){
if (node==null){
return;
}
afterThreadedNodes(node.getLeft());
afterThreadedNodes(node.getRight());
if (node.getLeft()==null){
node.setLeftType(1);
node.setLeft(pre);
}
if (pre!=null&&pre.getRight()==null){
pre.setRightType(1);
pre.setRight(node);
}
pre=node;
}
/**
* 前序线索遍历
*/
public void preThreadeVisit(){
Node node=this.root;
while (node!=null){
//打印当前结点
System.out.println(node);
while (node.getLeftType()==0){
node=node.getLeft();
System.out.println(node);
}
if (node.getRightType()==1){
node=node.getRight();
}else if (node.getRight()==null){
//线索化前序遍历的最后一个结点的right一定为null,所以遍历完毕 退出循环
break;
}
}
}
/**
* 中序线索遍历
*/
public void midThreadeVisit(){
Node node=this.root;
while (node!=null){
//找到左子树最左边的结点
while (node.getLeftType()==0){
node=node.getLeft();
}
//打印当前结点
System.out.println(node);
//若当前结点的右子结点为后继结点 就一直打印
while (node.getRightType()==1){
node=node.getRight();
System.out.println(node);
}
//替换这个结点
node=node.getRight();
}
}
/**
* 后序线索遍历
*/
public void afterThreadeVisit(){
// Node node=this.root;
// boolean flag=false;
// while (node!=null){
// //找到左子树最左边的结点
// while (node.getLeftType()==0){
// node=node.getLeft();
// }
//
// //打印当前结点
// System.out.println(node);
// if (node.getRightType()==1){
// node=node.getRight();
// }else if(){
// node=root;
// }
//
// if (node==root&&flag){
// break;
// }
// }
}
/**
* 前序遍历
*/
public void preVisit(){
if(this.root!=null){
this.root.preVisit();
}else {
System.out.println("二叉树为空!");
}
}
/**
* 中序遍历
*/
public void midVisit(){
if(this.root!=null){
this.root.midVisit();
}else {
System.out.println("二叉树为空!");
}
}
/**
* 后序遍历
*/
public void afterVisit(){
if(this.root!=null){
this.root.afterVisit();
}else {
System.out.println("二叉树为空!");
}
}
}
3.测试代码(每测试一种类型的线索化,其它类型的线索化代码注释掉,要不然出错)
public class ThreadedBinaryTreeTest {
public static void main(String[] args) {
Node root=new Node(1,"Tom");
Node node2=new Node(3,"Jack");
Node node3=new Node(6,"Cindy");
Node node4=new Node(8,"Petter");
Node node5=new Node(10,"Jucy");
Node node6=new Node(14,"Rose");
//创建二叉树
root.setLeft(node2);
root.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
//对二叉树进行中序线索化
ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
threadedBinaryTree.setRoot(root);
//上面创建的二叉树中序遍历出来应该是 8 3 10 1 14 6
/**
* 测试线中序索化过后叶子结点 是不是已经被线索化
* 例如node5(10,Jucy) 它的前驱结点应该是node2(3,Jack) 后继结点应该是root(1,Tom)
*/
threadedBinaryTree.midThreadedNodes();
Node preNode=node5.getLeft();
Node afterNode=node5.getRight();
System.out.println("(中序线索化)node5(10,Jucy)的前驱结点为:"+preNode);
System.out.println("(中序线索化)node5(10,Jucy)的后继结点为:"+afterNode);
threadedBinaryTree.midThreadeVisit();
//上面创建的二叉树前序遍历出来应该是 1 3 8 10 6 14
/**
* 测试线前序索化过后叶子结点 是不是已经被线索化
* 例如node5(10,Jucy) 它的前驱结点应该是node4(8,Petter) 后继结点应该是node3(6,Cindy)
*/
threadedBinaryTree.preThreadedNodes();
Node preNode1=node5.getLeft();
Node afterNode1=node5.getRight();
System.out.println("(前序线索化)node5(10,Jucy)的前驱结点为:"+preNode1);
System.out.println("(前序线索化)node5(10,Jucy)的后继结点为:"+afterNode1);
threadedBinaryTree.preThreadeVisit();
//上面创建的二叉树后序遍历出来应该是 8 10 3 14 6 1
/**
* 测试线后序索化过后叶子结点 是不是已经被线索化
* 例如node5(10,Jucy) 它的前驱结点应该是node4(8,Petter) 后继结点应该是node2(3,Jack)
*/
threadedBinaryTree.afterThreadedNodes();
Node preNode2=node5.getLeft();
Node afterNode2=node5.getRight();
System.out.println("(后序线索化)node5(10,Jucy)的前驱结点为:"+preNode2);
System.out.println("(后序线索化)node5(10,Jucy)的后继结点为:"+afterNode2);
threadedBinaryTree.afterThreadeVisit();
}
}