java实现线索化二叉树
线索二叉树:
在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序、中序、后序或层次等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。
存储结构:
线索二叉树中的线索能记录每个结点前驱和后继信息。为了区别线索指针和孩子指针,在每个结点中设置两个标志leftType和rightType。
当leftType和rightType为0时,left和right分别是指向左孩子和右孩子的指针;否则,left是指向结点前驱的线索(pre),right是指向结点的后继线索(suc)。由于标志只占用一个二进位,每个结点所需要的存储空间节省很多。
现将二叉树的结点结构重新定义如下:
其中:leftType=0 时left 指向左儿子;leftType=1 时left 指向前驱;rightType=0 时right指向右儿子;rightType=1 时right指向后继。
java代码实现:
//线索二叉树
public class ThreadeBinaryTreeDemo {
public static void main(String[] args) {
HeroNode node1 = new HeroNode(1, "宋江");
HeroNode node2 = new HeroNode(2, "吴用");
HeroNode node3 = new HeroNode(3, "卢俊义");
HeroNode node4 = new HeroNode(4, "林冲");
HeroNode node5 = new HeroNode(5, "关胜");
HeroNode node6 = new HeroNode(6, "2左");
BinaryTree root = new BinaryTree(node1);
node1.setLeft(node2);
node1.setRight(node3);
node3.setRight(node4);
node3.setLeft(node5);
node2.setLeft(node6);
//前序线索化二叉树
root.preThreadedNodes();
//前序遍历显示中序线索化二叉树
root.preOrderThreadeNode();
//中序线索化二叉树
// root.infixThreadedNodes();
//中序遍历显示中序线索化二叉树
// root.infixOrderThreade();
}
}
//创建二叉树
class BinaryTree {
private HeroNode root; //二叉树的根节点
private HeroNode pre = null; //前驱
public BinaryTree(HeroNode root) {
this.root = root;
}
//前序遍历
public void preOrder() {
if (root == null) {
System.out.println("二叉树为空,前序遍历为空。");
} else {
root.preOrder();
}
}
//中序遍历
public void infixOrder() {
if (root == null) {
System.out.println("二叉树为空,中序遍历为空");
} else {
root.infixOrder();
}
}
//后序遍历
public void postOrder() {
if (root == null) {
System.out.println("二叉树为空,后序遍历为空");
} else {
root.postOrder();
}
}
//前序遍历查找
public HeroNode preOrderSreach(int no) {
if (root != null) {
return root.preOrderSreach(no);
} else {
return null;
}
}
//中序遍历查找
public HeroNode infixOrderSreach(int no) {
if (root != null) {
return root.infixOrderSreach(no);
} else {
return null;
}
}
//后序遍历查找
public HeroNode postOrderSreach(int no) {
if (root != null) {
return root.postOrderSreach(no);
} else {
return null;
}
}
//删除结点
//先判断当前的root是不是待删除结点 如果是就删除整颗二叉树,如果不是再调用结点的删除方法
public Boolean delNode(int no) {
if (root == null) {
return false;
} else {
if (root.getId() == no) {
root = null;
return true;
} else {
return root.delNode(no);
}
}
}
//重载一个没有参数的方法
public void infixThreadedNodes() {
infixThreadedNodes(root);
}
public void preThreadedNodes() {
preThreadedNodes(root);
}
//前序遍历 前序线索化二叉树
public void preOrderThreadeNode() {
if (root == null) {
System.out.println("二叉树为空,前序线索化遍历失败");
return;
}
HeroNode node = root;
while (node.getRight() != null) {
System.out.println(node);
while (node.getLeftType() == 0) {
System.out.println(node.getLeft());
node = node.getLeft();
}
while (node.getRightType() == 1) {
node = node.getRight();
}
}
System.out.println(node);
}
//线索化前序二叉树
public void preThreadedNodes(HeroNode node) {
if (node == null) {
return;
}
//线索化当前结点
if (node.getLeft() == null) {
node.setLeft(pre);
node.setLeftType(1);
}
if (pre != null && pre.getRight() == null) {
pre.setRight(node);
pre.setRightType(1);
}
//!!! 每处理一个节点的时候让当前结点是下一个结点的前驱结点
pre = node;
//线索化左子树
if (node.getLeftType() != 1) {
preThreadedNodes(node.getLeft());
}
//线索化右子树
if (node.getRightType() != 1) {
preThreadedNodes(node.getRight());
}
}
//遍历中序线索化二叉树
public void infixOrderThreade() {
if (root == null) {
System.out.println("二叉树为空树!");
return;
}
HeroNode node = 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 infixThreadedNodes(HeroNode node) {
//node == null , 没有办法线索化
if (node == null) {
return;
}
//线索化左子树
infixThreadedNodes(node.getLeft());
//线索化当前结点
if (node.getLeft() == null) {
node.setLeft(pre);
node.setLeftType(1);
}
if (pre != null && pre.getRight() == null) {
pre.setRight(node);
pre.setRightType(1);
}
//!!! 每处理一个节点的时候让当前结点是下一个结点的前驱结点
pre = node;
//线索化右子树
infixThreadedNodes(node.getRight());
}
}
//创建节点的类
class HeroNode {
private int id;
private String name;
private HeroNode left; // 默认为空
private HeroNode right; // 默认为空
//如果leftType=0 表示left指向左子树,如果lefTypet=1表示left指向前驱
//如果rightType=0 表示right指向右子树,如果rightType=1表示right指向后继
private int leftType;
private int rightType;
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;
}
public HeroNode(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HeroNode getLeft() {
return left;
}
public void setLeft(HeroNode left) {
this.left = left;
}
public HeroNode getRight() {
return right;
}
public void setRight(HeroNode right) {
this.right = right;
}
@Override
public String toString() {
return "HeroNode{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
/**
* 前序遍历思路
* 1.输出当前节点
* 2.判断段当前节点的左子节点是否为空,递归前序遍历
* 3.判断当前节点的右子节点是否为空,递归前序遍历
*/
public void preOrder() {
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
/**
* 中续遍历思路
* 1.判断段当前节点的左子节点是否为空,递归中序遍历
* 2.输出当前节点
* 3.判断当前节点的右子节点是否为空,递归中续遍历
*/
public void infixOrder() {
if (this.left != null) {
this.left.infixOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.infixOrder();
}
}
/**
* 后续遍历思路
* 1.判断段当前节点的左子节点是否为空,递归后序遍历
* 2.判断当前节点的右子节点是否为空,递归后续遍历
* 3.输出当前节点
*/
public void postOrder() {
if (this.left != null) {
this.left.postOrder();
}
if (this.right != null) {
this.right.postOrder();
}
System.out.println(this);
}
/**
* 前序遍历查找的思路
* 1.判断当前节点是否为待查找节点,如果是直接返回,
* 2.判断当前节点的左子节点是否为空,不为空则向左递归前序查找,找到则返回,找不到则返回null
* 3.判断当前节点的右子节点是否为空,不为空则向右递归前序查找,找到则返回,找不到则返回null
*
* @param no 带查找的节点id
* @return 返回该节点的信息
*/
public HeroNode preOrderSreach(int no) {
// System.out.println("前序遍历的次数~~~");
if (this.id == no) {
return this;
}
HeroNode heroNode = null;
//判断当前节点的左子节点是否为空,不为空则向左递归前序查找,找到则返回,找不到则返回null
if (this.left != null) {
heroNode = this.left.preOrderSreach(no);
}
if (heroNode != null) {
return heroNode;
}
//判断当前节点的右子节点是否为空,不为空则向右递归前序查找,找到则返回,找不到则返回null
if (this.right != null) {
heroNode = this.right.preOrderSreach(no);
}
return heroNode;
}
/**
* 中序遍历查找的思路
* 1.判断当前节点的左子节点是否为空,不为空则向左递归中序查找,找到则返回,找不到则返回null,
* 2.判断当前节点是否为待查找节点,如果是直接返回
* 3.判断当前节点的右子节点是否为空,不为空则向右递归中序查找,找到则返回,找不到则返回null
*
* @param no 带查找的节点id
* @return 返回该节点的信息
*/
public HeroNode infixOrderSreach(int no) {
HeroNode heroNode = null;
//判断当前节点的左子节点是否为空,不为空则向左递归中序查找,找到则返回,找不到则返回null
if (this.left != null) {
heroNode = this.left.infixOrderSreach(no);
}
if (heroNode != null) {
return heroNode;
}
// System.out.println("中序遍历的次数~~~");
if (this.id == no) {
return this;
}
//判断当前节点的右子节点是否为空,不为空则向右递归中序查找,找到则返回,找不到则返回null
if (this.right != null) {
heroNode = this.right.infixOrderSreach(no);
}
return heroNode;
}
/**
* 后序遍历查找的思路
* 1.判断当前节点的左子节点是否为空,不为空则向左递归后序查找,找到则返回,找不到则返回null,
* 2.判断当前节点的右子节点是否为空,不为空则向右递归后序查找,找到则返回,找不到则返回null
* 3.判断当前节点是否为待查找节点,如果是直接返回
*
* @param no 带查找的节点id
* @return 返回该节点的信息
*/
public HeroNode postOrderSreach(int no) {
HeroNode heroNode = null;
//判断当前节点的左子节点是否为空,不为空则向左递归后序查找,找到则返回,找不到则返回null
if (this.left != null) {
heroNode = this.left.postOrderSreach(no);
}
if (heroNode != null) {
return heroNode;
}
//判断当前节点的右子节点是否为空,不为空则向右递归后序查找,找到则返回,找不到则返回null
if (this.right != null) {
heroNode = this.right.postOrderSreach(no);
}
if (heroNode != null) {
return heroNode;
}
// System.out.println("后序遍历的次数~~~~");
if (this.id == no) {
return this;
}
return heroNode;
}
/**
* 删除结点的思路
* 1.因为二叉树是单向是的,所以删除的时候只能删除当前结点的下一个结点。
* 2.如果当前的左子节点不为空,判断当前的左子结点是否为待删除结点,如果是领this.left=null,让后返回true。
* 3.如果当前的右子节点不为空,判断当前的右子结点是否为待删除结点,如果是就领this.right=null,让后返回true。
* 4.如果 2 , 3 都没有找到待删除结点就向左递归,
* 5.如果向左递归没有找到就向右递归
*
* @param no 待删除结点的id
* @return 返回是否删除成功
*/
public Boolean delNode(int no) {
if (this.left != null && this.left.id == no) {
this.left = null;
return true;
}
if (this.right != null && this.right.id == no) {
this.right = null;
return true;
}
if (this.left != null) {
if (this.left.delNode(no)) {
return true;
}
}
if (this.right != null) {
if (this.right.delNode(no)) {
return true;
}
}
return false;
}
}