1.顺序存储二叉树
从存储数据来看,数组存储方式和树的存储方式可以相互转换。
要求
- 要求以二叉树的方式来存放数组:arr [1,2,3,4,5,6,7]
- 要求在遍历数组arr时,仍然可以以前序遍历、中序遍历和后序遍历的方式完成节点的遍历
顺序存储二叉树的特点
①. 顺序二叉树通常只考虑完全二叉树
②. 第n个元素的左子节点为2n+1
③. 第n个元素的右子节点为2n+2
④. 第n个元素的父节点为(n-1)/2
⑤. n表示二叉树中的第几个元素(按0开始编号)
代码实现
以数组方式存储二叉树
public class ArrayBinaryTree {
private int[] arr;//存储数据节点的数组
public ArrayBinaryTree(int[] arr) {
this.arr = arr;
}
public void preOrder() {
preOrder(0);
}
//完成顺序存储二叉树的前序遍历
//index表示数组的下标
public void preOrder(int index){
if (arr==null||arr.length==0){
System.out.println("数组为空,不能按照二叉树的前序遍历");
return;
}
//输出当前数组元素
System.out.println(arr[index]);
//向左递归遍历
if (index*2+1<arr.length){
preOrder(2*index+1);
}
//向右递归遍历
if (index*2+2<arr.length){
preOrder(2*index+2);
}
}
//中序
public void infixOrder(int index){
if (arr==null||arr.length==0){
System.out.println("数组为空,不能按照二叉树的前序遍历");
return;
}
//向左递归遍历
if (index*2+1<arr.length){
infixOrder(2*index+1);
}
//输出当前数组元素
System.out.println(arr[index]);
//向右递归遍历
if (index*2+2<arr.length){
infixOrder(2*index+2);
}
}
//后序
public void postOrder(int index){
if (arr==null||arr.length==0){
System.out.println("数组为空,不能按照二叉树的前序遍历");
return;
}
//向左递归遍历
if (index*2+1<arr.length){
postOrder(2*index+1);
}
//向右递归遍历
if (index*2+2<arr.length){
postOrder(2*index+2);
}
//输出当前数组元素
System.out.println(arr[index]);
}
}
测试代码
public class ArrBinaryTreeDemo {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7};
ArrayBinaryTree tree = new ArrayBinaryTree(arr);
tree.preOrder();// 1 2 4 5 3 6 7
tree.infixOrder(0);//4 2 5 1 6 3 7
tree.postOrder(0);//4 5 2 6 7 3 1
}
}
2. 线索化二叉树
概念
- 1)n个节点的二叉链表中含有n+1【公式2n-(n-1)=n+1】个空指针域。利用二叉链表中的空指针域,存放指向结点在牟宗遍历次序下的前驱和后驱节点的指针(这种附加的指针称为“线索”)
- 2)这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可以分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种
- 3)一个节点的前一个节点,称为前驱节点
- 4)一个节点的后一个节点,称为后继节点
- 5)下图中,中序遍历,3的前驱节点是8,后继节点是10
例
将数列{1,3,6,8,10,14}构建成一颗二叉树,含有n+1=7个空指针域

问题分析:
- 对上面的二叉树进行中序遍历,数列为{8,3,10,11,14,6}
- 但是6,8,10,14这几个节点的左右指针,并没有完全利用上
- 如果我们希望充分利用各个节点上的左右指针,让各个节点可以指向自己的前后节点该怎么办呢
- 这个时候我们的解决方案是:==线索化二叉树==
说明
当线索化二叉树后,Node节点的属性left和right,有如下情况
- 1)left指向的是左子树,也可能是指向的前驱节点,比如①节点left指向的左子树,而10节点的left指向的就是前驱节点
- 2)right指向的是右子树,也可能是指向后继节点,比如①节点right指向的是右子树,而10节点的right指向的是后继节点
代码实现
线索化代码实现
节点THeroNode
/**
* @author DSH
* @date 2020/5/13
* @description 线索化二叉树 二叉树节点
*/
public class THeroNode {
private int no;
private String name;
private THeroNode left;
private THeroNode right;
//说明
//1 如果leftType == 0 表示指向的是左子树,如果为1则表示指向前驱节点
//2 如果rightType == 0 表示指向的是右子树,如果为1表示指向的是后继节点
private int leftType = 0;
private int rightType = 0;
public THeroNode(int hNo, String hName){
this.no = hNo;
this.name = hName;
}
//省略get set代码
...setxxx();
...getxxx();
}
线索化二叉树TBinaryTree
public class TBinaryTree {
private THeroNode root;
//为了实现线索化排序,需要创建要给指向当前节点的前驱节点的指针
//在递归进行线索化时,pre 总是保留前一个接地那
private THeroNode pre = null;
public void setRoot(THeroNode root) {
this.root = root;
}
public void threadedNodes(){
this.threadedNodes(root);
}
//编写对二叉树进行中序线索化的方法
/**
*
* @param node 当前需要线索化的node
*/
public void threadedNodes(THeroNode node){
if (node==null){//空 不能线索化
return;
}
//中序
// (1) 先线索化左子树
threadedNodes(node.getLeft());
// (2) 再线索化当前节点
//先处理当前节点的前驱节点
//以8节点为例 8.left =null 8.leftType = 1
if (node.getLeft()==null){
//让当前节点的左指针指向前驱节点
node.setLeft(pre);
//修改当前节点的左指针的类型,指向前驱节点
node.setLeftType(1);
}
//处理后继节点
if (pre!=null&&pre.getRight()==null){
//让前驱节点的右指针指向当前节点
pre.setRight(node);
//修改
pre.setRightType(1);
}
//每处理一个节点后,让当前节点是下一个节点的前驱节点
pre = node;
// (3) 最后线索化右子树
threadedNodes(node.getRight());
}
}
测试代码
/**
* @author DSH
* @date 2020/5/13
* @description 中序线索化二叉树
*/
public class ThreadedBinaryTreeDemo {
public static void main(String[] args) {
THeroNode root = new THeroNode(1, "tom");
THeroNode node2 = new THeroNode(3, "jack");
THeroNode node3 = new THeroNode(6, "smith");
THeroNode node4 = new THeroNode(8, "mary");
THeroNode node5 = new THeroNode(10, "king");
THeroNode node6 = new THeroNode(14, "stephen");
root.setLeft(node2);
root.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
TBinaryTree tBinaryTree = new TBinaryTree();
tBinaryTree.setRoot(root);
//线索化二叉树
tBinaryTree.threadedNodes();
//以10号节点测试,测试前驱后继节点
System.out.println(node5.getLeft());//3
System.out.println(node5.getRight());//1
}
}
遍历
TBinary添加遍历方法
//中序遍历线索化二叉树
public void listThreadedBinaryTree(){
//定义一个遍历,存储当前遍历的节点 从root开始
THeroNode 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();
}
}
测试代码
//线索化之后 不能使用原来的遍历方式
System.out.println("使用线索化方式遍历线索化二叉树");
tBinaryTree.listThreadedBinaryTree();