声明:以下是学的尚硅谷网课并结合网上资料所记的笔记。可能会有一些错误,发现了会修改。
顺序存储二叉树
概念:指的是使用顺序表(数组)存储二叉树。需要注意的是,顺序存储只适用于完全二叉树,换句话说,只有完全二叉树才可以使用顺序表存储。数组存储方式和树的存储方式可以相互转换。 如下:
特点:
- 第n个元素的左子结点为2n+1;
- 第n个元素的右子结点为2n+2;
- 第n个元素的父结点为(n-1) / 2;
- n表示二叉树中第n个元素(按0开始编号)。
例如:给你一个数组{1, 2, 3, 4, 5, 6, 7},要求以二叉树的前序遍历方式来遍历数组。 -> 1,2,4,5,3,6,7
代码如下:
public void preOrder(int index){
if(arr == null || arr.length == 0){
System.out.println("数组为空");
}
System.out.println("arr[index]"); //输出当前元素
if(2*index + 1 < arr.length){
preOrder(2*index + 1); //向左子结点递归
}
if(2*index + 2 < arr.length){
preOrder(2*index + 2); //向右子结点递归
}
}
线索化二叉树
在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如前序、中序、后序等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。
- n个结点的二叉链表中含有n+1(
2n-(n-1)=n+1
)个空指针域,利用二叉链表中的空指针域,存放指向该节点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为线索)。 - 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树。线索二叉树可分为前序线索二叉树,中序线索二叉树和后序线索二叉树三种。
- 在某种遍历方式下,一个结点的前一个结点,称为前驱结点,后一个节点称为后继结点。
构建中序线索化二叉树过程: 先将所有的结点右子结点为空的指针域指向它的后继结点,然后将所有结点左指针域为空的指针域指向它的前驱结点。下图所示为线索化二叉树。
代码:
public class ThreadBinaryTree {
//线索化时记录前一个结点
private Node preNode;
//结点
static class Node {
String data; //数据域
Node left; //左指针域
Node right; //右指针域
boolean isLeftThread = false; //左指针域类型 false:指向子结点 true:前驱或后继线索
boolean isRightThread = false; //右指针域类型 false:指向子结点 true:前驱或后继线索
Node(String data) {
this.data = data;
}
}
//通过数组构造一个二叉树(完全二叉树)
static Node createBinaryTree(String[] array, int index) {
Node node = null;
if(index < array.length) {
node = new Node(array[index]);
node.left = createBinaryTree(array, index * 2 + 1);
node.right = createBinaryTree(array, index * 2 + 2);
}
return node;
}
//中序线索化二叉树
void inThreadOrder(Node node) {
if(node == null) {
return;
}
//处理左子树
inThreadOrder(node.left);
//左指针为空,将左指针指向前驱结点
if(node.left == null) {
node.left = preNode;
node.isLeftThread = true;
}
//前一个结点的后继结点指向当前结点
if(preNode != null && preNode.right == null) {
preNode.right = node;
preNode.isRightThread = true;
}
preNode = node;
//处理右子树
inThreadOrder(node.right);
}
// 中序遍历线索二叉树,按照后继结点方向遍历(思路:找到最左子结点开始)
void inThreadList(Node node) {
//1、找中序遍历方式开始的结点
while(node != null && !node.isLeftThread) {
node = node.left;
}
while(node != null) {
System.out.print(node.data + ", ");
//如果右指针是线索
if(node.isRightThread) {
node = node.right;
} else { //如果右指针不是线索,找到右子树开始的节点
node = node.right;
while(node != null && !node.isLeftThread) {
node = node.left;
}
}
}
}
// 中序遍历线索二叉树,按照前驱结点方向遍历(思路:找到最右子结点开始倒序遍历)
void inPreThreadList(Node node) {
//1、找最后一个结点
while(node.right != null && !node.isRightThread) {
node = node.right;
}
while(node != null) {
System.out.print(node.data + ", ");
//如果左指针是线索
if(node.isLeftThread) {
node = node.left;
} else { //如果左指针不是线索,找到左子树开始的结点
node = node.left;
while(node.right != null && !node.isRightThread) {
node = node.right;
}
}
}
}
public static void main(String[] args) {
String[] array = {"A", "B", "C", "D", "E", "F", "G", "H", "I"};
Node root = createBinaryTree(array, 0);
ThreadBinaryTree tree = new ThreadBinaryTree();
tree.inThreadOrder(root); //创建中序线索化二叉树
System.out.println("按后继结点方向中序遍历线索二叉树结果:");
tree.inThreadList(root);
System.out.println();
System.out.println("按前驱结点方向中序遍历线索二叉树结果:");
tree.inPreThreadList(root);
}
}
优势
- 利用线索二叉树进行中序遍历时,不必采用堆栈处理,速度较一般二叉树的遍历速度快,且节约存储空间。
- 任意一个结点都能直接找到它的前驱和后继结点。
不足
- 结点的插入和删除麻烦,且速度也较慢。
- 线索子树不能共用。
小结
- 线索化的实质就是将二叉链表中的空指针改为指向前驱结点或后继结点的线索;
- 线索化的过程就是修改二叉链表中空指针的过程,可以按照前序、中序、后序的方式进行遍历,分别生成不同的线索二叉树;
- 有了线索二叉树之后,我们再次遍历时,就相当于操作一个双向链表。
- 使用场景:如果我们在使用二叉树过程中经常需要遍历二叉树或者查找节点的前驱节点和后继节点,可以考虑采用线索二叉树存储结构。
--------------------------------------- 个人学习笔记----------------------------------------