1.面临的问题:
将数列[1,3,6,8,10,14] 构建成一颗二叉树?
2.问题分析:
- 当我们对上述二叉树进行中序遍历时,数列为[8,3,10,1,6,14]
- 6,8,10,14这几个结点左右指针并没有很好的利用上。
- 如果考虑能够让每个结点充分利用左右指针指向自己的前后结点怎么去解决呢
这时只能选择使用线索二叉树。
3.线索二叉树介绍:
在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序、中序、后序等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。
对于n个结点的二叉树,在二叉链存储结构中有n+1个空链域,利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树。
这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。
一个结点的前一个结点,称之为前驱结点,一个结点后一个结点称之为后继结点。
4.线索二叉树的应用:
5.线索化二叉树的遍历
代码实现:
package clue;
/**
* @author WuChenGuang
*/
public class HeroNode {
private int no;
private String name;
private HeroNode left;
private HeroNode right;
/**
* 如果是0,表示指向的是左子树, 1表示指向的前驱
*/
private int noLeft;
/**
* 如果是0表示指向右子树, 1表示指向的是后继
*/
private int noRight;
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;
}
public int getNoLeft() {
return noLeft;
}
public void setNoLeft(int noLeft) {
this.noLeft = noLeft;
}
public int getNoRight() {
return noRight;
}
public void setNoRight(int noRight) {
this.noRight = noRight;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
/**
* 删除节点
*
* 删除的可能是一个叶子结点,也可能是非叶子结点
*/
public void delNode(int no){
/*
1.如果当前结点左节点不为空,并且左子结点正好是要删除的,this.left=null;
2.如果当前结点右节点不为空,并且右子结点正好是要删除的,this.right=null;
3. 1,2,都没有执行,左子树递归
4. 向右递归找右子树
*/
if (this.left !=null && this.left.no == no){
this.left = null;
return;
}
if (this.right !=null && this.right.no == no){
this.right = null;
return;
}
// 判断,防止异常
if (this.left !=null){
this.left.delNode(no);
}
if (this.right !=null){
this.right.delNode(no);
}
}
/**
* 前序遍历所有
*/
public void preSelect(){
System.out.println(this);
if (this.left !=null){
this.left.preSelect();
}
if (this.right !=null){
this.right.preSelect();
}
}
/**
*根据编号前序查找
*/
public HeroNode preSelect(int no){
if (this.no == no){
return this;
}
HeroNode temp = null;
// 往左递归
if (this.left !=null){
temp = this.left.preSelect(no);
}
if (temp !=null){
return temp;
}
// 往右递归
if (this.right !=null){
temp = this.right.preSelect(no);
}
return temp;
}
}
package clue;
/**
* @author WuChenGuang
*/
public class ClueBinaryTree {
private HeroNode root;
/**
* 当前结点前驱节点指针
*/
private HeroNode pre = null;
public void setRoot(HeroNode root) {
this.root = root;
}
/**
* 调用这个方法等于把非线索化二叉树转线索化二叉树
*/
public void clueNode() {
this.clueNode(root);
}
/**
* 把普通二叉树转线索化二叉树
*/
public void clueNode(HeroNode node) {
// 判断是否可以线索化当前节点
if (node == null) {
return;
}
// 先线索化左子树
clueNode(node.getLeft());
// 处理当前结点前驱
if (node.getLeft() == null) {
node.setLeft(pre);
node.setNoLeft(1);
}
// 处理当前结点后继
if (pre != null && pre.getRight() == null) {
pre.setRight(node);
pre.setNoRight(1);
}
// 保证处理完一个结点后,就需要让当前节点作为下一个结点的前驱
pre = node;
clueNode(node.getRight());
}
/**
* 遍历线索化二叉树
*/
public void clueList() {
// 临时结点
HeroNode node = root;
while (node != null) {
// 向左查询头结点
while (node.getNoLeft() == 0) {
node = node.getLeft();
}
System.out.println(node);
while (node.getNoRight() == 1) {
node = node.getRight();
System.out.println(node);
}
node = node.getRight();
}
}
}
package clue;
/**
* @author WuChenGuang
*/
public class TestApp {
public static void main(String[] args) {
HeroNode root = new HeroNode(1, "吕布");
HeroNode node2 = new HeroNode(3, "貂蝉");
HeroNode node3 = new HeroNode(6, "曹操");
HeroNode node4 = new HeroNode(8, "刘备");
HeroNode node5 = new HeroNode(10, "关羽");
HeroNode node6 = new HeroNode(14, "张飞");
root.setLeft(node2);
root.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
ClueBinaryTree clueBinaryTree = new ClueBinaryTree();
clueBinaryTree.setRoot(root);
// 把普通二叉树线索化为线索二叉树
clueBinaryTree.clueNode();
// 获取10的前驱和后继
HeroNode left = node5.getLeft();
HeroNode right = node5.getRight();
System.out.println("10号结点前驱是:" + left.getNo() + " 后继是:" + right.getNo());
// 把线索化二叉树按照中序遍历查询
clueBinaryTree.clueList();
}
}
运行结果: