一、顺序存储二叉树
1.相关介绍
顺序存储二叉树是使用数组存储二叉树的元素,二叉树节点之间的关系通过数组元素的下标索引来体现。在存储结构上,数组依照二叉树“自顶向下,自左向右”的顺序存储每个节点元素。
举个例子,将下图所示的二叉树存储在数组中,得到以下结果:
2.顺序存储二叉树特点
- 通常考虑完全二叉树、满二叉树;
- 二叉树中第n个节点的左、右子节点的下标分别为:2n+1、2n+2;
- 第n个节点的父节点下标为floor((n-1)/2);
- n表示二叉树中节点的编号,对应数组中元素的下标索引,从0开始;
这里的几个特点一定要清晰,后面编写遍历算法需要用到!
比如元素1的编号为n=0,其左右子节点编号分别为:2n+1=1、2n+2=2,对应元素2,3;其中节点的编号对应数组存储中的元素下标。
二、遍历算法
之前在https://blog.csdn.net/qq_43665602/article/details/126943631中说过二叉树的通用遍历算法,这里我们既想将二叉树存储在数组中,又想在遍历数组时可以实现和二叉树遍历一样的效果,即对顺序存储二叉树实现前序遍历、中序遍历以及后序遍历。其实在代码逻辑方面他的遍历过程和之前说到的二叉树遍历没什么区别,只是在实现时,节点之间的父子关系我们需要通过他们各自对应的下标索引来确定(也就是顺序存储二叉树特点中的第二、三点)。
下面我再简单描述一下几种遍历算法的实现过程:
因为我们使用数组存储二叉树,所以父子节点之间的关系通过元素的下标去确定。此外,在代码编写时,我们需要在最开始判断数组是否为空数组,若为空数组,则不进行遍历。
1.前序遍历
- 打印当前元素;
- 若左子节点索引没有发生数组越界,则进行左子节点递归遍历;
- 若右子节点索引没有发生数组越界,则进行右子节点递归遍历;
// 前序遍历
public void preOrder(int n) {
if(this.arr==null || this.arr.length==0){
System.out.println("数组为空,无法遍历!");
}
System.out.print(this.arr[n]+" "); // 输出当前元素
if ((2 * n + 1) < this.arr.length) { // 左节点递归遍历
this.preOrder(2 * n + 1);
}
if ((2 * n + 2) < this.arr.length) { // 右节点递归遍历
this.preOrder(2 * n + 2);
}
}
2.中序遍历
- 若前元素的左子节点索引没有发生数组越界,则进行左子节点递归遍历;
- 打印当前元素;
- 若前元素的右子节点索引没有发生数组越界,则进行右子节点递归遍历;
// 中序遍历
public void middleOrder(int n) {
if(this.arr==null || this.arr.length==0){
System.out.println("数组为空,无法遍历!");
}
if ((2 * n + 1) < this.arr.length) { // 左节点递归遍历
this.middleOrder(2 * n + 1);
}
System.out.print(this.arr[n]+" "); // 输出当前元素
if ((2 * n + 2) < this.arr.length) { // 右节点递归遍历
this.middleOrder(2 * n + 2);
}
}
3.后序遍历
- 若当前元素的左子节点索引没有发生数组越界,则进行左子节点递归遍历;
- 若前元素的右子节点索引没有发生数组越界,则进行右子节点递归遍历;
- 打印当前元素;
// 后序遍历
public void postOrder(int n) {
if(this.arr==null || this.arr.length==0){
System.out.println("数组为空,无法遍历!");
}
if ((2 * n + 1) < this.arr.length) { // 左节点递归遍历
this.postOrder(2 * n + 1);
}
if ((2 * n + 2) < this.arr.length) { // 右节点递归遍历
this.postOrder(2 * n + 2);
}
System.out.print(this.arr[n]+" "); // 输出当前元素
}
三、完整代码
我们先对文章开头图中的二叉树,手动分析一下几种遍历结果:
前序遍历:1,2,4,5,3,6,7
中序遍历:4,2,5,1,6,3,7
后序遍历:4,5,2,6,7,3,1
这里需要注意,二叉树的根节点是存储在数组中下标为0的位置,所以无论调用哪种遍历方法,都是需要传入索引n=0。为了后期调用方便,在构建顺序存储二叉树类时,对每种遍历方法进行了重载,这样在主函数中调用时便不用传入参数了。
package com.north.tree;
import java.util.Arrays;
public class SequentialBinaryTreeDemo {
public static void main(String[] args){
System.out.println("顺序存储二叉树...");
int[] arr={1,2,3,4,5,6,7};
SequentialBinaryTree arr_tree=new SequentialBinaryTree(arr);
System.out.println("前序遍历:");
arr_tree.preOrder();
System.out.println();
System.out.println("中序遍历:");
arr_tree.middleOrder();
System.out.println();
System.out.println("后序遍历:");
arr_tree.postOrder();
System.out.println();
}
}
class SequentialBinaryTree {
private int[] arr; // 数组
public SequentialBinaryTree(int[] arr) {
this.arr = arr;
}
public void setArr(int[] arr) {
this.arr = arr;
}
// 前序遍历
public void preOrder(int n) {
if(this.arr==null || this.arr.length==0){
System.out.println("数组为空,无法遍历!");
}
System.out.print(this.arr[n]+" "); // 输出当前元素
if ((2 * n + 1) < this.arr.length) { // 左节点递归遍历
this.preOrder(2 * n + 1);
}
if ((2 * n + 2) < this.arr.length) { // 右节点递归遍历
this.preOrder(2 * n + 2);
}
}
// 重载前序遍历
public void preOrder(){
this.preOrder(0);
}
// 中序遍历
public void middleOrder(int n) {
if(this.arr==null || this.arr.length==0){
System.out.println("数组为空,无法遍历!");
}
if ((2 * n + 1) < this.arr.length) { // 左节点递归遍历
this.middleOrder(2 * n + 1);
}
System.out.print(this.arr[n]+" "); // 输出当前元素
if ((2 * n + 2) < this.arr.length) { // 右节点递归遍历
this.middleOrder(2 * n + 2);
}
}
// 重载中序遍历
public void middleOrder(){
this.middleOrder(0);
}
// 后序遍历
public void postOrder(int n) {
if(this.arr==null || this.arr.length==0){
System.out.println("数组为空,无法遍历!");
}
if ((2 * n + 1) < this.arr.length) { // 左节点递归遍历
this.postOrder(2 * n + 1);
}
if ((2 * n + 2) < this.arr.length) { // 右节点递归遍历
this.postOrder(2 * n + 2);
}
System.out.print(this.arr[n]+" "); // 输出当前元素
}
// 重载后序遍历
public void postOrder(){
this.postOrder(0);
}
}
遍历输出:
顺序存储二叉树...
前序遍历:
1 2 4 5 3 6 7
中序遍历:
4 2 5 1 6 3 7
后序遍历:
4 5 2 6 7 3 1