描述:
- 对于n个结点的二叉树,在二叉链存储结构中有n+1个空链域;
- 利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的指针,这些指针称为线索。(前序/中序/后序遍历:以中间根节点的遍历次序来分类)
- 加上线索的二叉树称为线索二叉树。
应用案例说明:
将下面的二叉树,进行中序线索二叉树。中序遍历的数列为 {8,3,10,1,14,6}
由二叉树可知,中序遍历的结果为:{8,3,10,1,14,6},按照遍历后的数组顺序,如果当前节点有左右子节点忽略,如果没有,则加上前驱节点和后继节点。
代码实现:
1 中序线索二叉树的实现方法:
//编写对二叉树进行中序线索化的方法
public void threadedNodes(HeroNode node){
//如果node == null,不能线索化
if(node == null){
return;
}
// (一)先线索化左子树
threadedNodes(node.getLeft());
//(二)线索化当前结点[有难度]
//1.处理当前结点的前驱结点
if(node.getLeft() == null){
//让当前结点的左指针指向前驱结点
node.setLeft(pre);
//修改当前结点的左指针的类型,指向前驱结点
node.setLeftType(1);
}
//2.处理后序结点
if(pre!= null && pre.getRight() == null){
//让前驱结点的右指针指向当前结点
pre.setRight(node);
pre.setRightType(1);
}
//3.每处理一个结点后,让当前结点是下一个结点的前驱结点
pre = node;
//(三)再线索化右子树
threadedNodes(node.getRight());
}
- 节点类:
//创建节点类
class HeroNode{
private int no;
private String name;
private HeroNode left;
private HeroNode right;
//说明:1.如果leftType == 0表示指向的是左子树,如果1则表示指向前驱节点
//2.如果rightType == 0表示指向的是右子树,如果为1,表示指向的是后驱节点
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 no, String name) {
this.no = no;
this.name = name;
}
//封装属性
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 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;
}
//重写toString方法
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
//编写前序遍历的方法
public void preOrder(){
System.out.println(this); //先输出当前父节点
if(this.left != null){ //递归向左子树前序遍历
this.left.preOrder();
}
if(this.right != null){
this.right.preOrder(); //递归向右子树前序遍历
}
}
//编写中序遍历的方法
public void infixOrder(){
if(this.left != null){
this.left.infixOrder(); //遍历,如果当前节点的左子节点不为空,则递归中序遍历的方法
}
System.out.println(this);
if(this.right != null){
this.right.infixOrder();; //遍历,如果当前节点的右子节点不为空,则递归中序遍历的方法
}
}
//编写后序遍历方法
public void postOrder(){
if(this.left != null){
this.left.postOrder();
}
if(this.right != null){
this.right.postOrder();
}
System.out.println(this);
}
//前序遍历查找的方法
public HeroNode preOrderSearch(int no){
System.out.println("进入前序遍历:");
//比较当前节点是不是要查找的节点
if(this.no == no){ //如果是,跳出循环,程序结束
return this;
}
HeroNode resNode = null;
if(this.left != null){ //如果左子节点不为空,怎进行左递归查找
resNode = this.left.preOrderSearch(no);
}
if(resNode != null){ //如果结果变量不为空,则表示找到了,返回该结果
return resNode;
}
if(this.right != null){
resNode = this.right.preOrderSearch(no); //如果右子节点不为空,怎进行左递归查找
}
return resNode;
}
//中序遍历查找的方法
public HeroNode infixOrderSearch(int no){
HeroNode resNode = null;
if(this.left != null){
resNode = this.left.infixOrderSearch(no);
}
if(resNode != null){
return resNode;
}
System.out.println("进入中序查找");
if(this.no == no){
return this;
}
if(this.right != null){
resNode = this.right.infixOrderSearch(no);
}
return resNode;
}
//后序遍历的查找方法
public HeroNode postOrderSearch(int no){
HeroNode resNode = null;
if(this.left != null){
resNode = this.left.postOrderSearch(no);
}
if(resNode != null){
return resNode;
}
if(this.right != null){
resNode = this.right.postOrderSearch(no);
}
if(resNode != null){
return resNode;
}
System.out.println("进入后序查找");
if(this.no == no){
return this;
}
return resNode;
}
//添加删除节点的方法
public void delNode(int no){
//2.如果当前结点的左子节点不为空,并且左子节点就是要删除的节点,就将this.left = null;并且就返回
if(this.left != null && this.left.no == no){
this.left = null;
return;
}
//3.如果当前结点的右子节点不为空,并且右子节点就是要删除的节点,就将this.left = null;并且就返回
if(this.right != null && this.right.no == no){
this.right = null;
return;
}
//4.如果没有找到,向左子树进行递归删除
if(this.left != null){
this.left.delNode(no);
}
//5.如果还没有找到,向右子树进行递归删除
if(this.right != null){
this.right.delNode(no);
}
}
- 二叉树类:
//定义二叉树的类(线索化功能的二叉树)
class ThreadedBinaryTree{
private HeroNode root;
//为了实现线索化,需要创建要给指向当前前驱结点的指针
private HeroNode pre = null; //在进行递归线索化,pre总是指向前一个结点
public void setRoot(HeroNode root) { //创建set方法
this.root = root;
}
//重载一把threadedNodes方法
public void threadedNodes(){
this.threadedNodes(root);
}
//遍历线索化二叉树的方法
public void threadedList(){
//定义一个变量,存储当前遍历的结点,容root开始
HeroNode node = root;
while(node != null){
//循环找到leftType == 1的结点,第一个找到的就是8结点
//后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化处理的有效结点
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 threadedNodes(HeroNode node){
//如果node == null,不能线索化
if(node == null){
return;
}
// (一)先线索化左子树
threadedNodes(node.getLeft());
//(二)线索化当前结点[有难度]
//1.处理当前结点的前驱结点
if(node.getLeft() == null){
//让当前结点的左指针指向前驱结点
node.setLeft(pre);
//修改当前结点的左指针的类型,指向前驱结点
node.setLeftType(1);
}
//2.处理后序结点
if(pre!= null && pre.getRight() == null){
//让前驱结点的右指针指向当前结点
pre.setRight(node);
pre.setRightType(1);
}
//3.每处理一个结点后,让当前结点是下一个结点的前驱结点
pre = node;
//(三)再线索化右子树
threadedNodes(node.getRight());
}
//前序遍历
public void preOrder(){
if(this.root != null){
this.root.preOrder();
}else{
System.out.println("二叉树为空,无法遍历");
}
}
//中序遍历
public void infixOrder(){
if(this.root != null){
this.root.infixOrder();
}else{
System.out.println("二叉树为空,无法遍历");
}
}
//后序遍历
public void postOrder(){
if(this.root != null){
this.root.postOrder();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
//前序遍历查找
public HeroNode preOderSearch(int no){
if(root != null){
return root.preOrderSearch(no);
}else{
return null;
}
}
//中序遍历查找
public HeroNode infixOrderSearch(int no){
if(root != null){
return root.infixOrderSearch(no);
}else{
return null;
}
}
//后序遍历查找
public HeroNode postOderSearch(int no){
if(root != null){
return root.postOrderSearch(no);
}else{
return null;
}
}
//删除节点
public void delNode(int no){
if(root != null){
//如果只有一个root节点,这里立即判断root是不是要删除的节点
if(root.getNo() == no){
root = null;
}else{
//递归删除
root.delNode(no);
}
}else {
System.out.println("空树,不能删除");
}
}
}