先看一个问题
将数列{1,3,6,8,10,14}构建成一颗二叉树
如图
问题分析:
1.当我们对上边的二叉树进行中序遍历时,数列为{8,3,10,1,14,6}
2.但是6,8,10,14这几个节点的左右指针并没有完全利用上
3.如果我们希望充分的利用各个节点的左右指针,让各个节点可以指向自己的前后节点怎么办
解决方法-线索二叉树
线索二叉树基本介绍
1.n个节点的二叉链表中含有n+1公式2n-(n-1)=n+1个空指针域。利用二叉链表中的空指针域,存放指向节点在某种遍历次序下的前驱和后继节点的指针(这种附加的指针被称为线索)
一个节点有两个指向 n个节点需要n-1个线连接
即空指针域2n-(n-1)
2.这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树,根据线索的性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种
3.一个节点的前一个节点,称为前驱节点
4.一个节点的后一个节点,称为后继节点
说明:当线索化二叉树后,node节点的属性left和right,有如下情况
1.left指向的左子树,也可能是指向的前驱节点
2.right指向的是右子树,也可能是后继节点
遍历线索化二叉树
说明:对前面的中序线索化的二叉树,进行遍历
分析:因为线索化后,各个节点的指向有变化,因此原来的遍历方式不能使用,需要使用新的方式遍历线索化二叉树,各个节点可以通过线性方式遍历,因此无需使用递归方式,这样也提高了遍历的效率,遍历的次序应当和中序遍历保持一致
首先前序、中序、后序的二叉树的线索化的方式大致相同的
只是由于遍历方式的不同,递归左右节点和处理当前节点的位置不同
具体可以看着代码对比着自己画出的线索化的二叉树来进行分析
线索化遍历的时候可能有些问题,就是定义的存储当前指针的赋值问题
中序遍历和前序遍历较好理解
后序遍历还需要一个新的parent来解决该问题,下边代码中也有体现,
后序的规则如下:
1.若节点是二叉树的根,则其后继节点为空
2.若节点是其双亲的右孩子,或是其双亲的左孩子,且其双亲没有右子树,则其后继节点为其双亲节点
3.若节点是其双亲的左孩子,且其双亲有右子树,则其节点为双亲右子树上,按后序遍历列出的第一个节点
话不多说,我们下边的代码也是基于上图的二叉树来进行的,代码中也有一些遍历撒撒的,可以挑重点阅读。
测试代码二叉树的创建方式我还是手动创建的,就不放在这里了
//定义BinaryTree
//实现了线索化功能的二叉树
class BinaryTree{
private DataNode root;
//为了实现线索化,需要定义一个指向当前节点的前驱节点的指针
private DataNode pre=null;
public void setPre(DataNode pre) {
this.pre = pre;
}
public DataNode getPre() {
return pre;
}
public void setRoot(DataNode root) {
this.root = root;
}
public void threadedNodes(){
threadedNodes(this.root);
}
public void threadedPostNodes(){
threadedPostNodes(this.root);
}
public void threadPreNodes(){
threadedPreNodes(this.root);
}
//前序线索遍历二叉树的方法
public void threadedPreList(){
//定义节点,存储当前遍历的节点,初始为root;
DataNode node=root;
while(node!=null){
while (node.getLeftType()==0){
System.out.println(node);
node=node.getLeft();
}
System.out.println(node);
node=node.getRight();
}
}
//中序遍历线索化二叉树的方法
public void threadMidList(){
//定义一个节点,存储当前遍历的节点,初始时为root
DataNode node=root;
while(node!=null){
//找到lefttype==1的节点,第一个就是8节点
//后面随着遍历node会变化,因为当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();
}
}
//后序遍历线索化二叉树的方法
/**
* 1.若节点是二叉树的根,则其后继节点为空
* 2.若节点是其双亲的右孩子,或是其双亲的左孩子,且其双亲没有右子树,则其后继节点为其双亲节点
* 3.若节点是其双亲的左孩子,且其双亲有右子树,则其节点为双亲右子树上,按后序遍历列出的第一个节点
*/
public void ThreadedPostList(){
//定义一个节点存储遍历的节点,初始化时间为root
DataNode 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);
}
if(node==root){
node=null;
}else if(node==node.getParent().getRight() || (node==node.getParent().getLeft() && node.getParent().getRight()==null)){
node=node.getParent();
}else if(node==node.getParent().getLeft() && node.getParent().getRight()!=null){
List dataNodes=new ArrayList();
node.getParent().getRight().postOrderList(dataNodes);
node=dataNodes.get(0);
}
}
}
//前序线索化
public void threadedPreNodes(DataNode 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()==0){
threadedPreNodes(node.getLeft());
}
if(node.getRightType()==0){
threadedPreNodes(node.getRight());
}
}
//中序线索化
public void threadedNodes(DataNode node){
if(node==null){
return;
}
//一:先线索化左子树
threadedNodes(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
pre=node;
//三:线索化右子树
threadedNodes(node.getRight());
}
//后序线索化
public void threadedPostNodes(DataNode node){
if(node==null){
return;
}
//线索化左子树
threadedPostNodes(node.getLeft());
//线索化右子树
threadedPostNodes(node.getRight());
//处理当前节点
if(node.getLeft()==null){
node.setLeft(pre);
node.setLeftType(1);
}
//处理后继节点
if(pre!=null && pre.getRight()==null){
pre.setRight(node);
pre.setRightType(1);
}
pre=node;
}
//前序遍历
public void preOrder(){
if(this.root!=null){
this.root.preOrder();
}else{
System.out.println("二叉树为空");
}
}
//中序遍历
public void midOrder(){
if(this.root!=null){
this.root.midOrder();
}else{
System.out.println("nullpointexception");
}
}
//后序遍历
public void postOrder(){
if(this.root!=null){
this.root.postOrder();
}else{
System.out.println("nullpointexception");
}
}
//前序查找
public DataNode preOrdersearch(int num){
if(this.root!=null){
DataNode dataNode = this.root.preOrdersearch(num);
return dataNode;
}else{
System.out.println("null ");
return null;
}
}
//中序查找
public DataNode midOrdersearch(int num){
if(this.root!=null){
DataNode dataNode = this.root.midOrdersearch(num);
return dataNode;
}else{
System.out.println("null");
return null;
}
}
//后序查找
public DataNode postOrdersearch(int num){
if(this.root!=null){
DataNode dataNode = this.root.postOrdersearch(num);
return dataNode;
}else{
System.out.println("null");
return null;
}
}
/**删除节点
* 如果是叶子节点就直接删除该节点
*如果不是叶子节点 就直接删除该子树
*/
public void delNode(int num){
if(root==null){
System.out.println("树 为空");
return;
}
if(root.getNum()==num){
root=null;
return;
}
root.delNode(num);
}
}
//定义节点
class DataNode {
private int num;
private String name;
private DataNode left;
private DataNode right;
private int leftType;
private int rightType;
private DataNode parent;
//如果lefttype为0 则表示指向左子树 为1则为指向前驱节点
//如果righttype为0 则表示指向右子树 为1则表示为指向后继节点
public DataNode(int num, String name) {
this.num = num;
this.name = name;
}
public DataNode getParent() {
return parent;
}
public void setParent(DataNode 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;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DataNode getLeft() {
return left;
}
public void setLeft(DataNode left) {
this.left = left;
}
public DataNode getRight() {
return right;
}
public void setRight(DataNode right) {
this.right = right;
}
@Override
public String toString() {
return "DataNode{" +
"num=" + num +
", 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 midOrder() {
if (this.left != null) {
this.left.midOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.midOrder();
}
}
//后序遍历的方法
public void postOrder() {
if (this.left != null) {
this.left.postOrder();
}
if (this.right != null) {
this.right.postOrder();
}
System.out.println(this);
}
//后序遍历添加进list
public void postOrderList(List dataNodes){
if(this.left!=null && this.leftType==0){
this.left.postOrderList( dataNodes);
}
if(this.right!=null && this.rightType==0){
this.right.postOrderList( dataNodes);
}
dataNodes.add(this);
}
//前序遍历查找
public DataNode preOrdersearch(int num) {
//比较的次数
int count = 0;
System.out.println(++count);
if (this.num == num) {
return this;
}
DataNode resNode = null;
//判断左子节点是否为空
if (this.left != null) {
resNode = this.left.preOrdersearch(num);
}
if (resNode != null) {
return resNode;
}
if (this.right != null) {
resNode = this.right.preOrdersearch(num);
}
return resNode;
}
//中序遍历查找
public DataNode midOrdersearch(int num) {
DataNode resNode = null;
if (this.left != null) {
resNode = this.left.midOrdersearch(num);
}
if (resNode != null) {
return resNode;
}
//比较的次数
int count = 0;
System.out.println(++count);
if (this.num == num) {
return this;
}
if (this.right != null) {
resNode = this.right.midOrdersearch(num);
}
return resNode;
}
//后序遍历查找
public DataNode postOrdersearch(int num) {
DataNode resNode = null;
if (this.left != null) {
resNode = this.left.postOrdersearch(num);
}
if (resNode != null) {
return resNode;
}
if (this.right != null) {
resNode = this.right.postOrdersearch(num);
}
if (resNode != null) {
return resNode;
}
//比较的次数
int count = 0;
System.out.println(++count);
if (this.num == num) {
return this;
}
return null;
}
/**
* 递归删除节点
* 如果是叶子节点就删除该节点
* 如果不是叶子节点就删除该子树
*/
public void delNode(int num) {
//判断左子节点是否为空,是否权等于我们要删除的
if (this.left != null && this.left.num == num) {
this.left = null;
return;
}
//判断右子节点是否为空
if (this.right != null && this.right.num == num) {
this.right = null;
return;
}
//向左子树递归
if (this.left != null) {
this.left.delNode(num);
}
//向右子树递归
if (this.right != null) {
this.right.delNode(num);
}
}