1的左右=2,3;2的左=4;3、4是叶子
| 节点 | 子树完全? |
|---|---|
| 4 | 是 |
| 3 | 是 |
| 2 | 是 |
| 1 | 是 |
答案 = 4
1的左右=2,3;3的左=4;2、4是叶子
| 节点 | 子树完全? |
|---|---|
| 4 | 是 |
| 2 | 是 |
| 3 | 是 |
| 1 | 否 |
答案 = 3
做这道题,必须先分清三种二叉树:普通二叉树、完全二叉树、满二叉树。它们是层层递进的关系。
随便长,没有限制
除最后一层全满,最后一层从左到右连续
每一层都全满,没有任何空缺
这是本题的核心。我们要对每个节点判断「以它为根的子树是否完全」。关键思路:后序遍历,先算左右子,再根据左右子的信息推出当前节点。每个节点要带三个信息向上汇报:
isComp:我的子树是不是完全二叉树?isFull:我的子树是不是满二叉树(每层全满)?height:我的子树高度是多少?(空节点高度 = -1,叶子高度 = 0)isFull?因为父节点判断时,需要知道我的子树是不是「最后一层全满」——这正是 isFull 的含义!
设左子返回 (cL, fL, hL),右子返回 (cR, fR, hR),根据左右高度关系分六种情况:
hL = hR = -1 → 是叶子。完全 ✓ 且满 ✓,高度 0。每个叶子都算一棵完全二叉树。
hL = 0, hR = -1 → hL = hR + 1。左子必须完全(叶子天然完全),右子空天然满。完全 ✓(不满,因为最后一层没满)。高度 1。
完全二叉树不可能「有右子没左子」(最底层必须从左到右连续,左边空了右边不能有)。不完全 ✗。
hL = hR 且 fL && cR → 左子树每层全满,右子树从最左开始连续填充(允许最后一层不满)→ 完全 ✓,高度 hL+1。若右子也满(fL && fR)则同时是满二叉树!
hL = hR + 1。右子必须满(右子最后一层全满),左子必须完全。这样最后一层的节点全在左子,且从左到右连续。完全 ✓(不满)。高度 hL+1。
hL 和 hR 的差不是 0 也不是 1(左比右多2层以上,或右比左高),或者等高但左子不满 / 右子不完全 → 不完全 ✗。
if (hL == hR) comp = fL && cR; (等高→左满、右完全)else if (hL == hR + 1) comp = cL && fR; (左高一层→左完全右满)else comp = false; (其他都不行)
用一棵 8 节点的教学树演示后序遍历全过程。每个节点处理时会显示它收到左右子的三状态信息,然后判定自己是否完全。
/* * 完全二叉树子树计数 * 后序遍历,每个节点返回(isComp, isFull, height) * 空节点: (true, true, -1) * 时间 O(n) */ #include <bits/stdc++.h> using namespace std; const int N = 100005; int lc[N], rc[N]; // 左右儿子,0=空 int ans = 0; // 返回 {isComp, isFull, height} tuple<bool,bool,int> dfs(int u) { if (u == 0) return {true, true, -1}; // 空节点 auto [cL, fL, hL] = dfs(lc[u]); auto [cR, fR, hR] = dfs(rc[u]); bool comp, full; int h; if (hL == hR) { // 情况4: 等高 → 左满、右完全 comp = fL && cR; // 等高: 左子树满 + 右子树完全 full = fL && fR; h = hL + 1; } else if (hL == hR + 1) { // 情况5: 左高一层 comp = cL && fR; // 左完全, 右满 full = false; h = hL + 1; } else { // 情况6: 其他 comp = false; full = false; h = max(hL, hR) + 1; } if (comp) ans++; // 统计完全二叉树 return {comp, full, h}; } int main() { ios::sync_with_stdio(false); int n; cin >> n; for (int i = 1; i <= n; i++) cin >> lc[i] >> rc[i]; dfs(1); cout << ans << endl; return 0; }
isComp:直接回答题目(这棵子树完全吗?)。isFull:父节点判断时需要——如果父节点左右等高,要求左子满;如果父节点左比右高一层,要求右子满。没有 isFull,父节点没法判断。height:判断高度关系(等高?差一层?差太多?)必须用高度。
-Wl,-stack_size,0x1000000(macOS)或开大栈| 处理顺序 | 节点 | 左子(cL,fL,hL) | 右子(cR,fR,hR) | 情况 | 结果(comp,full,h) | 计数 |
|---|---|---|---|---|---|---|
| 1 | 4 | 空(T,T,-1) | 空(T,T,-1) | 叶子(等高) | (T,T,0) | 1 |
| 2 | 2 | 4(T,T,0) | 空(T,T,-1) | 左高一层 | (T,F,1) | 2 |
| 3 | 3 | 空(T,T,-1) | 空(T,T,-1) | 叶子(等高) | (T,T,0) | 3 |
| 4 | 1 | 2(T,F,1) | 3(T,T,0) | 左高一层 | (T,F,2) | 4 |
4 ✅| 处理顺序 | 节点 | 左子(cL,fL,hL) | 右子(cR,fR,hR) | 情况 | 结果(comp,full,h) | 计数 |
|---|---|---|---|---|---|---|
| 1 | 2 | 空(T,T,-1) | 空(T,T,-1) | 叶子 | (T,T,0) | 1 |
| 2 | 4 | 空(T,T,-1) | 空(T,T,-1) | 叶子 | (T,T,0) | 2 |
| 3 | 3 | 4(T,T,0) | 空(T,T,-1) | 左高一层 | (T,F,1) | 3 |
| 4 | 1 | 2(T,T,0) | 3(T,F,1) | 右比左高! | (F,F,2) | 3 |
3 ✅ 节点1的右子比左子高 → 不完全7 道题,检验你对完全二叉树和算法的理解。