🏗️

层序遍历与完全二叉树的数组存储

层序 = 一层一层看;完全二叉树 = 能按"座位号"整齐塞进数组

💡 打个比方

消防队检查一栋楼,不会"顺着楼梯一口气冲顶",而是一层查完再查下一层,每层从东到西挨个房间。把这套"一层一层"的规则用在树上,就是层序遍历。

而"完全二叉树"像一座座位号排得满满当当的阶梯影院:第 1 排 1 个座、第 2 排 2 个、第 3 排 4 个……座位号从左到右连号发。既然座位号这么规整,直接用数组下当代号就能管理整棵树——不用一根指针一根指针地连!

层序遍历:按"层"从上到下、每层从左到右访问每个节点。数组存储完全二叉树:用下标 1~n 依次存各节点,节点 i 的左孩子在 2i、右孩子在 2i+1、父亲在 i/2(向下取整)。

🖼️ 大图看明白

层序:一层一层看 1 ① 2 3 ② 4 5 6 7 ③ 层序输出:1 2 3 4 5 6 7 ① 先根 → ② 再第二层 → ③ 最后第三层 同一棵树塞进数组(下标从 1 开始) 下标 1 2 3 4 5 6 7 数组 1 2 3 4 5 6 7 i 的左孩子 → 下标 2i i 的右孩子 → 下标 2i + 1 i 的父亲 → 下标 i / 2(整除) 例:节点 3 → 左孩子 6、右孩子 7、父亲 1;节点 5 → 父亲 2

🧩 子任务 1:层序怎么走?

🎯 小目标:会手算一棵树的层序序列,并说清"队列"为什么是关键

规则很简单:先看根,再看它的孩子(从左到右),再看孩子的孩子……一层扫完再扫下一层。对示例树:1 → 2、3 → 4、5、6、7,输出 1 2 3 4 5 6 7。

问题:怎么保证"一层扫完才轮到下一层"?答案是排队(队列):根先排队 → 访问根时,把它的孩子按顺序排在队尾 → 队头的人被访问时,再把它的孩子排到队尾。先来先服务,天然就是一层一层的!

关键道具:队列(queue)——"先进先出"的队伍,后面第 13 课专门学。现在先记住:层序遍历 = 递归的兄弟,用队列一层层扩散。
✅ 小检查:把"示例树"的节点换成字母:根 A,左 B 右 C,B 的孩子 D、E。层序是什么?
看看答案
A B C D E。先 A,再第二层 B、C(从左到右),再第三层 D、E。

🧩 子任务 2:数组存储三公式

🎯 小目标:背下三公式,能秒答"i 的父亲/左孩子/右孩子在下标几"

完全二叉树按层序编号(1 号是根,2、3 号是它的左右孩子,4、5、6、7 号是下一层……),下标之间就有固定数学关系:

· 左孩子下标 = 2i · 右孩子下标 = 2i+1 · 父亲下标 = i/2(整除)

验证一下:3 号是 1 号的右孩子(2×1+1=3 ✓);5 号的父亲 = 5/2 = 2 ✓;4 号的左孩子 = 8(若存在)。

为什么只对完全二叉树成立:公式假设"编号连续、无空位"。一旦中间缺人(留空位),2i 指的就不是真正的左孩子了——所以普通二叉树一般用指针,完全二叉树(如堆、线段树)用数组,又快又省内存。
✅ 小检查:下标 10 的节点,它的父亲、左孩子、右孩子分别在下标几?
看看答案
父亲 = 10/2 = 5;左孩子 = 20;右孩子 = 21(后两个是否存在取决于树有多大)。

🧩 子任务 3:数组 ↔ 树 互转

🎯 小目标:给数组会画树,给树会写层序序列

来一道经典题。数组(下标从 1 开始)是:8 3 10 1 6 14 4 7 2 13(共 10 个数)。

给数组画树,按公式逐个"落座":下标 1 是根(8);2、3 是 1 的左右孩子(3、10);4、5 是 2 的左右孩子(1、6);6、7 是 3 的左右孩子(14、4);8、9 是 4 的左右孩子(7、2);10 是 5 的左孩子(13),11 不存在所以没有右孩子。看,最后一层的 7、2、13 也是从左到右坐的——这是一棵完全二叉树。

8 3 10 1 6 14 4 7 2 13 橙色 = 第 4 层,从左到右坐(完全二叉树) 同一棵树放进数组 下标 1 2 3 4 5 6 7 8 9 数组 8 3 10 1 6 14 4 7 2 … 10 13 层序序列 = 数组内容:8 3 10 1 6 14 4 7 2 13 验证:14 的父亲 = 6/2 = 3 号(值 10)✓;13 的父亲 = 10/2 = 5 号(值 6)✓

给树写数组:就是做一遍层序遍历——一层一层、从左到右编号,把每个节点的值按顺序写进数组的下一个空位。对上图这棵树,层序序列是 8 3 10 1 6 14 4 7 2 13,和数组内容一模一样!

双向通道:完全二叉树的"层序遍历输出" = "存它的数组内容"。所以存完全二叉树时,把层序序列直接写进数组就大功告成,一个指针都不用。
✅ 小检查:上例数组里,下标 6 的节点(值 14)的父亲是谁?下标 10 的节点在第几层?
看看答案
下标 6 的父亲 = 6/2 = 3 号(值是 10,正好 14 是 10 的左孩子 ✓)。第 1~3 层共 1+2+4 = 7 个节点,下标 10 > 7,所以它在第 4 层(第 4 层第 3 个位置)。

💻 完整代码

两段代码:第一段用队列做指针树的层序遍历;第二段把完全二叉树存进数组,用三公式秒查父亲和孩子。

// 代码 1:指针树的层序遍历(队列版)
#include <bits/stdc++.h>
using namespace std;

struct Node {
    int val;
    Node *l, *r;
    Node(int v) : val(v), l(nullptr), r(nullptr) {}
};

void bfs(Node *root) {
    if (!root) return;
    queue<Node*> q;
    q.push(root);              // 根先排队
    while (!q.empty()) {
        Node *p = q.front();   // 队头出列
        q.pop();
        cout << p->val << " ";  // 访问(打印)
        if (p->l) q.push(p->l); // 孩子按左→右排到队尾
        if (p->r) q.push(p->r);
    }
}

int main() {
    //      1
    //     / \
    //    2   3
    //   / \
    //  4   5
    Node *root = new Node(1);
    root->l = new Node(2);  root->r = new Node(3);
    root->l->l = new Node(4); root->l->r = new Node(5);
    bfs(root);   // 输出:1 2 3 4 5
    return 0;
}
// 代码 2:完全二叉树的数组存储(下标从 1 开始)
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1005;
int a[MAXN];
int n;

int main() {
    int vals[] = {0, 8, 3, 10, 1, 6, 14, 4, 7, 2, 13};
    n = 10;
    for (int i = 1; i <= n; i++) a[i] = vals[i];

    // 完全二叉树的层序遍历 = 按序打印数组
    for (int i = 1; i <= n; i++) cout << a[i] << " ";
    cout << endl;
    // 输出:8 3 10 1 6 14 4 7 2 13

    int i = 6;   // 查询下标 6 的节点
    if (i > 1) cout << "父亲 = " << a[i/2] << endl;
    // 输出:父亲 = 10
    if (2*i <= n) cout << "左孩子 = " << a[2*i] << endl;
    // 输出:左孩子 = 7
    if (2*i+1 <= n) cout << "右孩子 = " << a[2*i+1] << endl;
    // 输出:右孩子 = 2
    return 0;
}
易错点:数组下标 0 空着不用!根在 a[1]。查父亲前记得判断 i > 1(根没有父亲);查孩子前判断 2*i <= n(下标超出 n 就是没有这个孩子)。

✋ 你来试试

第 1 题:下标 7 的节点,它的左孩子、右孩子、父亲分别在下标几?
左孩子 = 2×7 = 14;右孩子 = 2×7+1 = 15;父亲 = 7/2 = 3(整除)。
第 2 题:10 个节点的完全二叉树(上例),第 4 层有几个节点?
前 3 层共 1+2+4 = 7 个,所以第 4 层 = 10 − 7 = 3 个(7、2、13)。
第 3 题:给数组 9 5 1 3 7(5 个数),画出对应的树,并写出下标 4 的父亲。
树:9 为根,左 5 右 1;5 的左孩子 3、右孩子 7;1 没有孩子。下标 4 的父亲 = 4/2 = 2 号(值 5)。
🎯 一句话记住:层序 = 排队一层层扫;完全二叉树 = 按座位号存数组,左孩子 2i、右孩子 2i+1、父亲 i÷2。