数据结构基础复习 二叉树重构:后序与中序序列建树技巧

判断对错 There exists a binary tree with 2016 nodes in total, and with 16 nodes having only one child.

ven a tree of degree 3. Suppose that there are 3 nodes of degree 2 and 2 nodes of degree 3. Then the number of leaf nodes must be ____.

If a general tree T is converted into a binary tree BT, then which of the following BT traversals gives the same sequence as that of the post-order traversal of T? A. Pre-order traversal B. In-order traversal C. Post-order traversal D. Level-order traversal

答案:C. Post-order traversal


核心结论(必须记住)

一般树 T转换成二叉树(左孩子-右兄弟表示法,LCRS)后:

  • 一般树的后序遍历
    👉 等价于
  • 对应二叉树的后序遍历

为什么是这样?

在左孩子-右兄弟表示法中:

  • 左指针 = 第一个孩子
  • 右指针 = 下一个兄弟

一般树的后序遍历规则

先访问所有孩子(从左到右)→ 再访问自己

而在二叉树中执行**后序遍历(Left → Right → Root)**时:

  • 左子树 = 第一个孩子及其子树
  • 右子树 = 兄弟节点链(同一层的其他孩子)

所以二叉树后序遍历刚好模拟:

👉 "先所有孩子 → 再自己"

最终答案

这题考了什么知识点

这道题本质考的是一个知识点:一般树(General Tree)与二叉树(Binary Tree)的转换,以及两者遍历之间的对应关系。


一、知识点1:一般树如何转换成二叉树(重点)

采用的是左孩子-右兄弟表示法(Left-Child Right-Sibling,LCRS)

转换规则只有两条:

  • 左指针(Left) → 指向第一个孩子
  • 右指针(Right) → 指向下一个兄弟

例如:

复制代码
一般树

      A
    / | \
   B  C  D
     / \
    E   F

转换成二叉树:

复制代码
      A
     /
    B
     \
      C
     / \
    E   D
     \
      F

这里:

  • A.left = B(第一个孩子)
  • B.right = C(兄弟)
  • C.right = D(兄弟)
  • C.left = E(第一个孩子)
  • E.right = F(兄弟)

二、知识点2:一般树遍历 与 二叉树遍历的对应关系(考试最爱)

这是最容易考选择题的地方。

需要记住下面这张表:

一般树遍历 转换后二叉树对应遍历
Preorder(先序) Preorder(先序)
Postorder(后序) Postorder(后序)

因此本题:

General Tree 的 Post-order

对应就是

Binary Tree 的 Post-order

所以选 C


三、为什么很多人会错?

很多同学容易误以为:

一般树 → 二叉树之后,遍历方式都会改变。

树变了,表示方式变了,但遍历逻辑并没有改变。

例如:

一般树

复制代码
      A
    / | \
   B  C  D

一般树先序

复制代码
A B C D

转换后二叉树

复制代码
    A
   /
  B
   \
    C
     \
      D

二叉树先序

复制代码
A B C D

完全一致。


一般树

复制代码
      A
    / | \
   B  C  D

一般树后序

复制代码
B C D A

转换后二叉树后序

复制代码
B C D A

也完全一致。


四、浙大数据结构考试真正考的是什么?

这类题几乎都是围绕下面几个知识点:

  1. 一般树转二叉树(LCRS)
    • 左孩子
    • 右兄弟
  2. 根据一般树画出二叉树
  3. 根据二叉树还原一般树
  4. 比较遍历序列
    • 哪种遍历相同?
    • 给定一种遍历,求另一种遍历。

建议记住的考试模板

  • 左 = 第一个孩子(First Child)
  • 右 = 下一个兄弟(Next Sibling)
  • General Tree 的 Preorder = Binary Tree 的 Preorder
  • General Tree 的 Postorder = Binary Tree 的 Postorder

掌握这几个结论,浙大数据结构中关于一般树与二叉树转换的选择题基本都能快速作答。

Among the following threaded binary trees (the threads are represented by dotted curves), which one is the postorder threaded tree? 这题考什么知识点

这道题考察的是线索二叉树(Threaded Binary Tree),更准确地说,是:

后序线索二叉树(Postorder Threaded Binary Tree)

它不是考树的转换,而是考遍历序列 + 线索建立规则


一、知识点1:什么是线索二叉树(最基础)

普通二叉树中,很多指针都是空的(NULL)。

例如:

复制代码
    A
   / \
  B   C
 /
D

D 的左右孩子都是 NULL。

线索二叉树就是:

把这些 NULL 指针改成指向遍历序列中的前驱或后继。

这样遍历时可以少用栈或递归。


二、知识点2:线索是按照哪种遍历建立的

这是本题真正考察的内容。

线索二叉树分为三种:

① 中序线索(Inorder Thread)

按照中序序列建立:

复制代码
Left -> 前驱
Right -> 后继

例如中序:

复制代码
D B A C

那么:

复制代码
D 的后继 = B
B 的前驱 = D

② 先序线索(Preorder Thread)

按照先序:

复制代码
A B D C

建立线索。


③ 后序线索(Postorder Thread)

按照后序:

复制代码
D B C A

建立线索。

本题问:

哪一个是 Postorder Threaded Tree

所以必须先写出:

后序遍历序列

再检查:所有虚线(Thread) 是不是连接 前驱后继


三、考试怎么判断?

一般步骤只有三步:

第一步 先写后序遍历。

复制代码
      A
     / \
    B   C
   / \
  D   E

后序:

复制代码
D E B C A

第二步

找空指针(NULL)

因为:

只有 NULL 才会变成 Thread。

不是 NULL 的左右孩子不会变。


第三步

检查虚线

复制代码
D ----> E

是不是后继?因为:后序:

复制代码
D E B C A

D 后继就是 E。说明这条线索正确。

五、考试记忆口诀(最重要)

线索永远来自遍历序列,而不是树的结构。

判断后序线索树时,牢记:

  1. 先写出 后序遍历序列
  2. 找出空指针(只有空指针才会变成线索)。
  3. 检查每条虚线是否连接到了该节点在后序遍历中的前驱或后继

因此,这道题考的核心知识点就是:

  • 线索二叉树(Threaded Binary Tree)
  • 后序遍历(Postorder Traversal)
  • 根据后序遍历建立前驱/后继线索,并识别后序线索二叉树

这个说法是错误的。

以下是详细解释:

  1. 二分查找的前提 :二分查找(Binary Search)的核心思想是每次直接访问数组中间的元素,从而将搜索范围缩小一半。**这要求数据结构必须支持随机访问(Random Access),**即通过索引在 O(1)的时间内直接访问任意元素。
  2. 单链表的限制:在单链表(Singly Linked List)中,不支持随机访问。要访问链表的中间节点,必须从头节点开始逐个遍历,这需要 O(N)的时间。
  3. 时间复杂度分析
    • 虽然在单链表中进行二分查找时,查找的次数(比较次数)确实是 O(log⁡N) 。
    • 但是,每次查找中间节点都需要花费 O(N) 的时间来遍历。
    • 因此,总的时间复杂度为 O(Nlog⁡N) ,而不是 O(log⁡N)。
    • 在最坏情况下(或者求平均情况时),在单链表中查找一个元素实际上就是普通的顺序查找,平均时间复杂度为 O(N)。

结论 :因为单链表不支持随机访问,所以在单链表中进行二分查找的平均时间复杂度不是 O(log⁡N)。

六、编程题

R6-1 Build Tree from Post- and In-order Sequences

分数 6

作者 DS课程组

单位 浙江大学

You are supposed to write a function of constructing a binary tree with given postorder and inorder sequences.

Format of function:

Tree BuildTree( int inorder\[\], int postorder\[\], int N );

where Tree is defined as the following:

typedef struct TreeNode *Tree;

struct TreeNode {

int Element;

Tree Left;

Tree Right;

};

The inorder and postorder sequences are stored in int inorder[] and int postorder[], respectively. The integer N is the number of nodes in the tree. The function BuildTree is supposed to return the pointer to the root of the constructed tree.

Sample program of judge:

#include <stdio.h>

#include <stdlib.h>

#define MAXN 10

typedef struct TreeNode *Tree;

struct TreeNode {

int Element;

Tree Left;

Tree Right;

};

Tree BuildTree( int inorder\[\], int postorder\[\], int N );

void Inorder_output( Tree T ); /* details omitted */

void Postorder_output( Tree T ); /* details omitted */

int main()

{

Tree T;

int inorderMAXN, postorderMAXN, N, i;

scanf("%d", &N);

for (i=0; i<N; i++) scanf("%d", &inorderi);

for (i=0; i<N; i++) scanf("%d", &postorderi);

T = BuildTree(inorder, postorder, N);

printf("Check:\n");

Inorder_output(T); printf("\n");

Postorder_output(T); printf("\n");

return 0;

}

/* Your function will be put here */

Sample Input:

7

1 2 3 4 5 6 7

2 3 1 5 7 6 4

Sample Output:

Check:

1 2 3 4 5 6 7

2 3 1 5 7 6 4

代码长度限制

这是一道经典的二叉树重构题。根据后序遍历(Post-order)中序遍历(In-order) 序列来重建树,核心思想是递归

核心思路分析:

  1. 找根节点:后序遍历的最后一个元素一定是当前子树的根节点。
  2. 划分左右子树:在中序遍历序列中找到该根节点的位置。根节点左边的序列就是左子树的中序遍历,右边的序列就是右子树的中序遍历。
  3. 计算子树大小:通过左子树在中序遍历中的节点个数,可以在后序遍历序列中准确划分出左子树和右子树的后序遍历序列。
  4. 递归构建:对左、右子树重复上述过程。

C语言代码实现:

cs 复制代码
Tree BuildTree( int inorder[], int postorder[], int N )
{
    // 1. 递归出口:如果节点数为0,说明是空树,返回 NULL
    if (N == 0) return NULL;

    // 2. 创建根节点:后序遍历的最后一个元素即为根节点
    Tree T = (Tree)malloc(sizeof(struct TreeNode));
    T->Element = postorder[N - 1];
    T->Left = NULL;
    T->Right = NULL;

    // 3. 在中序遍历中寻找根节点的位置,以划分左右子树
    int i;
    for (i = 0; i < N; i++) {
        if (inorder[i] == postorder[N - 1]) {
            break;
        }
    }

    // 4. i 即为左子树的节点个数
    // 递归构建左子树:
    // 中序遍历的左子树范围:inorder[0] 到 inorder[i-1],长度为 i
    // 后序遍历的左子树范围:postorder[0] 到 postorder[i-1],长度为 i
    T->Left = BuildTree(inorder, postorder, i);

    // 5. 递归构建右子树:
    // 中序遍历的右子树范围:inorder[i+1] 到 inorder[N-1],长度为 N - i - 1
    // 后序遍历的右子树范围:postorder[i] 到 postorder[N-2],长度为 N - i - 1
    T->Right = BuildTree(inorder + i + 1, postorder + i, N - i - 1);

    return T;
}

💡 避坑与提分指南:

  1. 指针偏移技巧 :注意代码中的 inorder + i + 1postorder + i。这是 C 语言中数组指针偏移的写法,等价于传入新数组的起始地址,避免了每次递归都拷贝数组,空间和时间效率极高。
  2. 右子树的后序起点 :极易写错的地方!右子树在后序遍历中的起点是 postorder + i(因为前 i 个位置已经被左子树占用了),千万不要写成 postorder + i + 1
  3. 内存分配 :别忘了给根节点 malloc 分配内存,并且初始化左右指针为 NULL,否则可能导致段错误(Segmentation Fault)。
核心模块 易错命题 / 考点 核心结论 / 避坑指南
二叉树重构 (Reconstruct) 后序 + 中序 →→ 二叉树 1. 后序尾 必为根; 2. 中序 找根,左为左子树,右为右子树; 3. 左子树节点数 ii 是关键:左子树后序为 post[0...i-1],右子树后序为 post[i...N-2]。 ⚠️ 避坑 :右子树后序起点是 post + i,千万别写成 post + i + 1
二叉树重构 (Reconstruct) 先序 + 中序 →→ 二叉树 1. 先序头 必为根; 2. 划分逻辑同上,左子树先序为 pre[1...i],右子树先序为 pre[i+1...N-1]。 ⚠️ 避坑后序+先序无法唯一确定二叉树(除非是满二叉树),考试若出此题直接选"无法确定"。
二叉搜索树 (BST) 删除节点后的中序前驱/后继 删除节点 XX 并用中序后继 YY 替换后, YY 会移动到 XX 的原位置。此时,YY 的中序前驱 就是 YY 新位置的左子树中的最大节点(千万别以为前驱还是原来的 XX , XX 已经没了!)。
线索二叉树 (Threaded Tree) 中序线索的左/右指针判断 在中序线索二叉树中: 1. 左指针是线索   ⟺  ⟺ 该节点无左孩子 (指向中序前驱)。 2. 右指针是线索   ⟺  ⟺ 该节点无右孩子 (指向中序后继)。 ⚠️ 避坑:找前驱/后继时,只看有没有孩子,别被树的整体形状迷惑。
并查集 (Disjoint Set) 按秩/高度合并的树高度上限 初始单节点高度为 0 时,对 nn 个节点执行按高度合并,树的最大高度绝不会超过 ⌊log⁡2n⌋⌊log2​n⌋ 。
二叉搜索树 (BST) 链表上的二分查找时间复杂度 单链表不支持随机访问(找中点需 O(N)O(N) ),因此即使采用二分思想,总时间复杂度也是 O(Nlog⁡N)O(NlogN),绝非 O(log⁡N)O(logN) 。
完全二叉树 最后两层节点数与总节点数的关系 若完全二叉树的最后两层均恰好有 nn 个节点,则倒数第二层必满,整棵树的总节点数必定为 3n−13n−1
满树 (Full Tree) 满 mm 叉树的最大高度计算 对于总节点数为 nn 的满 mm 叉树,内部节点数 i=n−1mi=mn−1​ 。为求最大高度,需将内部节点排成单链,最大高度 hmax=ihmax​=i 。
(Heap) 堆中最小/最大元素的位置 在最大堆(Max-Heap)中,最大值必定在根节点(索引1),但最小值只可能出现在叶子节点上(即索引大于 ⌊n/2⌋⌊n/2⌋ 的位置)。
循环队列 (Circular Queue) 队列大小的通用计算公式 无论是否发生回绕,队列元素个数公式恒为:Size = (rear - front + MaxSize) % MaxSize
(Stack) 括号匹配问题的数据结构 判断表达式中括号是否匹配,最经典且最适合的数据结构是栈(Stack)(利用后进先出特性)。

数据结构基础复习---TBT

考前最后叮嘱

  1. 重构树题 :一定要在草稿纸上画出树 !人的大脑很难在空想中准确追踪指针偏移,尤其是右子树的起点 post + i
  2. 线索二叉树题 :口诀**"无左即左线索,无右即右线索"**,直接看节点有没有孩子,不要绕弯子。
  3. 循环队列题 :看到 rear < front,脑子里立刻反应出**"发生回绕了"** ,直接用 (rear - front + MaxSize) % MaxSize 算。

For an in-order threaded binary tree, if the post-order and in-order

traversal sequences are FDEBGCA and FDBEACG, respectively.

Which pair of nodes' left links are both threads?

A. C and D. B. D and E. C. E and G. D. B and G.

To determine which pair of nodes' left links are both threads, we need to reconstruct the binary tree from the given post-order and in-order traversal sequences, and then analyze the left pointers.

Step 1: Reconstruct the Binary Tree

  • Post-order: F D E B G C A (Left, Right, Root)
  • In-order: F D B E A C G (Left, Root, Right)
  1. From the post-order sequence, the last node is A , so A is the root.
  2. In the in-order sequence, A divides the tree into:
    • Left subtree (in-order): F D B E
    • Right subtree (in-order): C G
  3. For the Left Subtree (in-order: F D B E):
    • The corresponding post-order sequence is F D E B.
    • The root of this subtree is B.
    • In in-order, B divides it into:
      • Left: F D (Root is D , Left is F, Right is empty)
      • Right: E
  4. For the Right Subtree (in-order: C G):
    • The corresponding post-order sequence is G C.
    • The root is C.
    • In in-order, C divides it into:
      • Left: empty
      • Right: G

The reconstructed tree structure is:

复制代码
        A
      /   \
     B     C
    / \     \
   D   E     G
  /
 F

Step 2: Analyze Left Threads in an In-order Threaded Binary Tree

In an in-order threaded binary tree, a node's left link is a thread if and only if the node has no left child. The thread points to its in-order predecessor.

Let's check the left links of all nodes:

  • A: Has left child B. (Not a thread)
  • B: Has left child D. (Not a thread)
  • C : Has no left child . (Left link is a thread)
  • D: Has left child F. (Not a thread)
  • E : Has no left child . (Left link is a thread)
  • F : Has no left child . (Left link is a thread)
  • G : Has no left child . (Left link is a thread)

The nodes whose left links are threads are: C, E, F, G.

Step 3: Evaluate the Options

  • A. C and D: D has a left child (F), so D's left link is not a thread.
  • B. D and E: D's left link is not a thread.
  • C. E and G: Both E and G have no left children, so both their left links are threads. (Correct)
  • D. B and G: B has a left child (D), so B's left link is not a thread.

Conclusion:

The correct pair is E and G.

Answer: C

要确定哪一对节点的左指针都是线索(threads),我们需要根据给定的后序和中序遍历序列来重构这棵二叉树,然后分析它们的左指针。

第一步:重构二叉树

  • 后序遍历:F D E B G CA(左子树, 右子树, 根节点)
  • 中序遍历 :F D B E A C G (左子树, 根节点, 右子树)
  1. 从后序遍历序列可知,最后一个节点是 A ,因此 A 是根节点。
  2. 在中序遍历序列中,A 将树划分为:
    • 左子树(中序):F D B E
    • 右子树(中序):C G
  3. 对于左子树 (中序:F D B E):
    • 对应的后序遍历序列为 F D E B
    • 该子树的根节点是 B
    • 在中序遍历中,B 将其划分为:
      • 左部分:F D (根节点为 D ,左孩子为 F,无右孩子)
      • 右部分:E
  4. 对于右子树 (中序:C G):
    • 对应的后序遍历序列为 G C
    • 根节点为 C
    • 在中序遍历中,C 将其划分为:
      • 左部分:空
      • 右部分:G

重构出的二叉树结构如下:

复制代码
        A
      /   \
     B     C
    / \     \
   D   E     G
  /
 F

第二步:分析中序线索二叉树中的左线索
在中序线索二叉树中,当且仅当一个节点没有左孩子时,它的左指针才会是线索(该线索会指向它的中序前驱节点)。

我们来检查所有节点的左指针:

  • A:有左孩子 B。(不是线索)
  • B:有左孩子 D。(不是线索)
  • C:没有左孩子。(左指针是线索)
  • D:有左孩子 F。(不是线索)
  • E没有左孩子 。(左指针是线索
  • F没有左孩子 。(左指针是线索
  • G没有左孩子 。(左指针是线索

左指针是线索的节点有:C, E, F, G

第三步:评估选项

  • A. C 和 D:D 有左孩子(F),所以 D 的左指针不是线索。
  • B. D 和 E:D 的左指针不是线索。
  • C. E 和 G:E 和 G 都没有左孩子,所以它们的左指针都是线索。(正确)
  • D. B 和 G:B 有左孩子(D),所以 B 的左指针不是线索。

结论:

正确的节点对是 E 和 G。

答案:C