跳至正文

408错题日记2


错题日记 02 · 数据结构

[DS] 数据结构的三大基石

Question

数据结构是具有()的数据元素的集合。

A. 性质相同 B. 特定关系 C. 相互关系 D. 数据项

My Answer

我的选择:A 理由:对数据结构根本定义记忆模糊,凭直觉选了”性质相同”。

Correct Answer

正确答案:B(特定关系) 正解

  1. 官方定义:数据结构(Data Structure)是相互之间存在一种或多种特定关系的数据元素的集合。
  2. 三要素模型:
    • 逻辑结构:描述数据元素之间的逻辑关系(如集合、线性、树形、图状)。
    • 物理结构(存储结构):逻辑结构在计算机中的映射(如顺序、链式、索引、散列)。
    • 数据的运算:施加在数据上的操作(如插入、删除、查找)。

Error Pattern

概念失真——对数据结构的根本定义记忆模糊,凭直觉选了”性质相同”。“性质相同”只是线性表或同类数据的特征,并不是数据结构的本质。数据结构的本质在于”数据元素之间的联系”。

Core Concept

  • 数据结构定义

Expected Context

  • 笔记路径:顺序表与链表、栈与队列、树与二叉树
  • 检索关键词:数据结构定义、特定关系、逻辑结构、存储结构、数据运算、三要素

Fix Plan

考研题中只要看到”数据结构定义”,脑海里立刻弹出公式:数据结构 = 数据元素 + 特定关系。不要被”性质相同”、“相互关系”这种似是而非的干扰项骗了。

同类题预警:若题目问”完整的数据结构应包含哪几个方面”,必须选包含逻辑结构、存储结构、数据运算的选项,缺一不可。

变式自测:若题目改为”数据的逻辑结构通常包括哪几种”,以下哪个选项正确?(提示:区分”逻辑结构”与”存储结构”的范畴。答案:集合、线性、树形、图状(B)。)


[DS] 连续存储与物理地址

Question

连续存储设计时,存储单元的地址()

A. 一定连续 B. 一定不连续 C. 不一定连续 D. 部分连续,部分不连续

My Answer

我的选择:C 理由:纯粹的审题失误,漏看了题干中的”连续存储”四个大字,大脑自动脑补成了”链式存储”或泛化的数据结构。

Correct Answer

正确答案:A(一定连续) 正解

  1. 题干已经明确了前提是连续存储设计(即顺序表的底层物理实现)。
  2. 在计算机内存中,所谓的”连续存储”,物理层面的直接体现就是:分配给该结构的每个元素的存储单元物理地址必须是连续的。
  3. 数组、顺序表等只要标明了”连续/顺序分配”,其相邻元素的地址差就是一个常数(元素大小)。

Error Pattern

致命漏读——纯粹的审题失误,漏看了题干中的”连续存储”四个大字,大脑自动脑补成了”链式存储”或泛化的数据结构,导致选了 C。

Core Concept

  • 顺序存储结构

Expected Context

  • 笔记路径:顺序表与链表
  • 检索关键词:顺序存储结构、连续存储设计、物理地址连续、链式存储、元素大小常数

Fix Plan

指读法:在 408 考场上,对题目中的定语(如”连续”、“非空”、“带头结点”)必须用笔圈出来。不要让大脑的潜意识惯性替你读题。

同类题预警:遇到”顺序分配/顺序存储”等价于”物理地址连续”,解题时将两者直接画等号;而”链式分配”则对应”物理地址不一定连续”。

变式自测:若题目改为”链式存储设计时,存储单元的地址()”?(提示:题干从”连续存储”换成”链式存储”,结论完全相反。答案:不一定连续(C)。)


[DS] 对数级外层与线性内层的嵌套

Question

【2022 年统考真题】下列程序段的时间复杂度是()

int sum=0;
for(int i=1; i<n; i*=2)
    for(int j=0; j<i; j++)
        sum++;

A. O(log n) B. O(n) C. O(n log n) D. O(n²)

My Answer

我的选择:A 理由:对嵌套 for 循环的计算过于依赖”套公式”直觉。看到外层 i *= 2 就认定是 O(log n),看到内层嵌套就觉得要相乘。忽略了内层循环的执行次数是受外层变量 i 动态控制的!

Correct Answer

正确答案:B(O(n)) 正解:面对内外层相关的嵌套循环,必须写出级数求和公式,绝不能简单相乘!

  1. 追踪外层变量:外层循环的 i 取值依次为 1, 2, 4, 8, …, 2^k。循环结束条件是 2^k ≥ n。
  2. 剥离内层执行次数:对于外层的每一个 i,内层循环 j 会执行 i 次。
  3. 整体求和(等比数列):总执行次数 T(n) = 1 + 2 + 4 + 8 + … + 2^k。首项为 1,公比为 2,总和为 2^(k+1) − 1。
  4. 化简结果:因为外层边界是 2^k ≈ n,代入上式得总执行次数约为 2n − 1。剥离常数后,时间复杂度为 O(n)。

Error Pattern

复杂度计算盲区——对嵌套 for 循环的计算过于依赖”套公式”直觉。看到外层 i *= 2 就认定是 O(log n),忽略了内层循环的执行次数是受外层变量 i 动态控制的。

Core Concept

  • 时间复杂度
  • 等比数列求和

Expected Context

  • 笔记路径:排序、查找、栈与队列
  • 检索关键词:时间复杂度、嵌套循环、牵连嵌套必求和、等比数列求和 2^(k+1)−1、O(n) 估计、内层终点 = 外层变量

Fix Plan

“牵连嵌套”必求和:如果内层循环的终点是外层循环的迭代变量(如 j < i),立刻放弃相乘法!老老实实把前三次执行的具体次数写出来:1 次、2 次、4 次……看到是等比数列,其加和的量级永远与最后那一项同阶!

同类题预警:若外层循环改为 for(int i=1; i<n; i*=3),内层为 for(int j=0; j<i; j++),同样使用等比数列求和:1 + 3 + 9 + … + 3^k ≈ (3n−1)/2,结果仍然是 O(n)——因为加和量级永远与最后一项同阶。

变式自测:若程序段改为外层线性、内层对数(j 互换 i 的角色),且两者独立相乘,时间复杂度为多少?(提示:外层线性 O(n),内层对数 O(log n)。答案:O(n log n)。)


[DS] 顺序表的物理本质与随机存取

Question

在顺序表中,逻辑上相邻的元素在物理位置上()

A. 相邻 B. 不相邻 C. 不一定相邻 D. 不确定

My Answer

我的选择:C 理由:再次漏看关键字”顺序表”!并且在脑海中将”随机存取(Random Access)“与”物理位置不一定相邻”错误缝合了,误以为能”随机找”就代表它在内存里”随机放”。

Correct Answer

正确答案:A(相邻) 正解

  1. 顺序表定义铁律:用一段地址连续的存储单元依次存储线性表的数据元素。这就决定了逻辑上相邻的元素,物理位置上绝对相邻。
  2. 随机存取的真相:正是因为物理上绝对相邻,且每个元素占用相同的空间大小 L,系统才能利用数学公式 LOC(a_i) = LOC(a_1) + (i−1) × L,在 O(1) 的时间内瞬间”空降”到任意元素的地址。

Error Pattern

因果倒置——再次漏看关键字”顺序表”,并且在脑海中将”随机存取(Random Access)“与”物理位置不一定相邻”错误缝合了,误以为能”随机找”就代表它在内存里”随机放”。

Core Concept

  • 顺序表
  • 随机存取

Expected Context

  • 笔记路径:顺序表与链表
  • 检索关键词:顺序表、地址连续、逻辑相邻物理相邻、随机存取、LOC 公式、O(1) 定位

Fix Plan

随机存取 ≠ 随机存储。随机存取是能力(通过下标一步到位),顺序存储是物理基础。没有物理的紧密排列,就没有下标计算的瞬间空降。

同类题预警:注意区分”顺序表”与”链表”在物理存储和存取特性上的对照关系——前者连续 + 随机存取,后者离散 + 顺序存取。一旦题干互换,答案完全反转。

变式自测:若题目改为”在单链表中,逻辑上相邻的元素在物理位置上()”?(提示:链式存储通过指针链接,物理地址不一定连续。答案:不一定相邻(C)。)


[DS] 头结点 vs 头指针的本质区别

Question

单链表中,增加头结点的目的是()

A. 标识单链表 B. 方便运算的实现 C. 使单链表中至少有一个结点 D. 用于标识起始结点的位置

My Answer

我的选择:D 理由:长期将头指针(Head Pointer)与头结点(Head Node)混为一谈。选了 D(标识起始位置),但那是头指针的活儿。

Correct Answer

正确答案:B(方便运算的实现) 正解

  1. 头指针(head):是指向链表第一个结点(无论是不是头结点)的变量/门牌号。它是必须存在的,用来标识单链表的起始位置。
  2. 头结点(Head Node):是在真正存放数据的第一个结点之前,人为插入的一个占位躯壳(不存有效数据)。
  3. 增加头结点的核心目的:
    • 统一操作:使得在第一个位置插入或删除结点时,操作语句与在链表中间操作时完全一致(都有前驱结点了),不需要写特殊的 if (p == head) 边界判断逻辑。
    • 空表一致:带头结点时,无论表是否为空,头指针均不为 NULL,处理更安全。

Error Pattern

术语张冠李戴——长期将头指针(Head Pointer)与头结点(Head Node)混为一谈。选了 D(标识起始位置),但那是头指针的活儿。

Core Concept

  • 头结点
  • 头指针

Expected Context

  • 笔记路径:顺序表与链表
  • 检索关键词:头结点、头指针、统一操作、空表一致、边界判断、带头结点

Fix Plan

  • 找路/标识起点?找头指针。
  • 方便写代码(不写特判)?找头结点。

同类题预警:考试中若讨论”指向链表第一个结点的指针”,指的一定是头指针;若讨论”插入在第一个结点之前的占位结点”,则是头结点。两者功能不同,不可互换。

变式自测:若题目改为”标识单链表起始位置的是()”?(提示:谁负责”指路”?谁是”躯壳”?答案:头指针(B)。)


[DS] 指针赋值的语法糖与时序陷阱

Question

在一个单链表中,已知指针 pq 分别指向链表中的相邻的两个结点。p 指向的结点是 q 指向结点的后继结点(即顺序为 q -> p),那么在这两个结点之间插入一个结点 s 的操作为()

A. s->next = p->next; p->next = s; B. p->next = s; s->next = q; C. s->next = q->next; q->next = p; D. q->next = s; s->next = p;

My Answer

我的选择:A 理由:被 C/C++ 的 -> 语法糖绕晕了,没有把 p->next 还原为物理意义上的”右手的链条”。加上选项具有强烈的视觉迷惑性,导致逻辑线断裂。

Correct Answer

正确答案:D(q->next = s; s->next = p;正解

  1. 翻译语法糖:在链表中,把 x->next 直接翻译成”x 伸出去的右手连着谁”。
  2. 梳理初态:题干说 p 是 q 的后继,即现在的状态是 q 的右手抓着 p(q->next == p)。目标是在中间塞入 s,变成 q -> s -> p
  3. 安全插入法则(先连后断):
    • 第一步:让新来的 s 伸出右手抓住 p。代码:s->next = p;(注意!绝对不是 p->next,我们要连的是 p 这个结点本身!)
    • 第二步:让前驱 q 断开原来的手,重新抓住 s。代码:q->next = s;
  4. 综合以上两步,选项 D 正确。选项 A 的 s->next = p->next 会让 s 指向 p 后面的结点,直接导致 p 被架空。

Error Pattern

结构体指针眩晕症——被 C/C++ 的 -> 语法糖绕晕了,没有把 p->next 还原为物理意义上的”右手的链条”。加上选项具有强烈的视觉迷惑性,导致逻辑线断裂。

Core Concept

  • 单链表插入
  • 指针操作

Expected Context

  • 笔记路径:顺序表与链表
  • 检索关键词:单链表插入、指针操作、先连后断、->next 语法糖、安全插入法则

Fix Plan

指针对接八字诀:“新接后继,前驱改新”。考场上看到 ->next 相关的题,必须、一定、绝对要画图!把 s 画在中间,画两条箭头,顺着箭头写代码,绝不会错。

同类题预警:若题目给的是 p 在 q 前面(即顺序为 p → q),则在 p 和 q 之间插入 s 的代码为 s->next = q; p->next = s;。务必看清题目中给出的指针顺序关系!

变式自测:已知单链表顺序为 head → a → b → c,要在 a 和 b 之间插入结点 s,正确的操作是?(提示:A 和 B 都能达到目的,但哪个顺序更安全?先连后断。答案:s->next = b; a->next = s;(A,先让 s 连上 b,再让 a 连上 s,防止断链)。)


[DS] 循环单链表的初始化空态

Question

在带头结点的循环单链表中,假设头结点为 Head。初始化链表为空,插入第一个结点 P 之后,链表的状态为()

A. P->next = NULL B. P->next = P C. Head->next->next = NULL D. P->next->next = P

My Answer

我的选择:B 理由:再次因为对头结点认识不够,潜意识里把 Head 当成了不占空间的头指针,以为插入第一个元素后,链表里只有 P 一个物理实体,所以选了自己指向自己的 P->next = P(这是不带头结点的循环链表状态)。

Correct Answer

正确答案:D(P->next->next = P正解

  1. 初始空表状态:带头结点的循环单链表为空时,Head 的指针域指向自己:Head->next = Head
  2. 插入结点 P(使用单链表插入公式,插在头结点之后):
    • P->next = Head->next;(此时 Head->next 也就是 Head,所以 P 的后继指向了 Head)
    • Head->next = P;(头结点的后继指向 P)
  3. 状态验证:现在的物理结构是 Head -> P -> Head 的死循环。选项 D 中,P->next 是 Head,那么 P->next->next 就是 Head->next,而 Head->next 指向 P。因此等式 P->next->next == P 完全成立。

Error Pattern

头结点隐形——再次因为对头结点认识不够,潜意识里把 Head 当成了不占空间的头指针,以为插入第一个元素后,链表里只有 P 一个物理实体,所以选了自己指向自己的 P->next = P(这是不带头结点的循环链表状态)。

Core Concept

  • 循环单链表
  • 头结点

Expected Context

  • 笔记路径:顺序表与链表
  • 检索关键词:循环单链表、带头结点、初始化空表 Head->next = Headnext->next 推导、死循环结构

Fix Plan

“带头结点”四个字值两分!只要看到带头结点,无论表里有没有真实数据,这个结构里永远至少有 1 个结点存在!做循环链表的题,必须在草纸上把带有 Head 的环画完整,再去推导 next->next

同类题预警:注意区分”带头结点”和”不带头结点”两种情况。不带头结点的循环单链表插入第一个结点时,P->next = P;带头结点的则是 Head -> P -> Head

变式自测:若题目改为”在”不带头结点”的循环单链表中插入第一个结点 P,链表的状态为()”?(提示:没有头结点,P 就是唯一的结点,自己指向自己形成环。答案:P->next = P(B)。)


[DS] 循环链表删除的唯一幸存者陷阱

Question

【2021 年统考真题】已知头指针 h 指向一个带头结点的非空单循环链表,结点结构为 [data | next]p 是尾指针,q 是临时指针。现要删除该链表的第一个元素,正确的语句序列是()

A. q = h->next; h->next = h->next->next; free(q); D. q = h->next; h->next = q->next; if(p==q) p=h; free(q);

My Answer

我的选择:A 理由:这是 408 命题组最经典的杀招——忘记考虑临界状态。只考虑了删除常规结点的跨接操作,完全忘记了如果被删除的结点是链表中唯一的一个物理元素时,尾指针 p 必须随之更新!

Correct Answer

正确答案:D(q = h->next; h->next = q->next; if(p==q) p=h; free(q);正解

  1. 常规删除逻辑:目标是删除头结点 h 后面的第一个数据结点 q。跨接代码为 h->next = q->next。这步没选错。
  2. 极其致命的尾指针更新:因为存在尾指针 p,如果链表中只有这一个元素(即 p 正好指向你要删除的 q,满足 p == q),当你把 q 给 free 掉之后,尾指针 p 就变成了野指针!
  3. 挽救机制:在释放 q 之前,必须加上特判:如果 p == q,说明链表删空了,必须将尾指针重置为指向头结点(p = h)。

Error Pattern

边界条件全盲——这是 408 命题组最经典的杀招:忘记考虑临界状态。只考虑了删除常规结点的跨接操作,完全忘记了如果被删除的结点是链表中唯一的一个物理元素时,尾指针 p 必须随之更新!

Core Concept

  • 循环单链表
  • 边界条件处理

Expected Context

  • 笔记路径:顺序表与链表
  • 检索关键词:循环单链表删除、尾指针更新、边界条件、唯一元素、野指针、if(p==q) p=h

Fix Plan

链表删除必问自己:删空了怎么办?凡是题干里带有尾指针、双指针的链表删除操作,选项里如果没有 if 判断边界情况,99% 是错的!永远要把”链表只有一个元素”作为代入检查的第一套测试用例。

同类题预警:凡是涉及尾指针的删除操作,必须自查是否触发了”唯一结点被删”的边界条件。类似地,若链表有头指针和尾指针同时维护,删除头结点时也需要检查尾指针是否需要更新。

变式自测:若题目改为”删除该链表的最后一个元素(尾指针 p 指向的结点)“,正确的语句序列应该包含什么关键判断?(提示:不仅要维护尾指针,还要考虑删除后链表为空的情况。答案:if(h->next == h) p = h;(B,当 h->next == h 时说明链表已空,需重置尾指针)。)


[DS] 反义词审题陷阱

Question

下面关于线性表的叙述中,不正确的是()

I. 线性表在链式存储时,查找第 i 个元素的时间同 i 的值成正比 II. 线性表在链式存储时,查找第 i 个元素的时间同 i 的值无关 III. 线性表在顺序存储时,查找第 i 个元素的时间同 i 的值成正比 IV. 线性表在顺序存储时,查找第 i 个元素的时间同 i 的值无关

A. I、II B. II、III C. III、IV D. I、IV

My Answer

我的选择:D 理由:极其可惜的失分!将题干的”不正确”看成了”正确”,直接选了描述完全正确的组合(链表按序找是 O(n) 选 I,顺序表随机存取是 O(1) 选 IV,所以选了 D)。

Correct Answer

正确答案:B(II、III) 正解:本题考查线性表两种物理结构的查找效率特性:

  1. 顺序表(顺序存储):具备随机存取特性,查找第 i 个元素直接通过公式计算物理地址,时间复杂度为 O(1),即同 i 的值无关(IV 对,III 错)。
  2. 链表(链式存储):具备顺序存取特性,必须从头指针顺藤摸瓜找过去,时间复杂度为 O(n),即同 i 的值成正比(I 对,II 错)。
  3. 题目要求选出不正确的,即 II 和 III。

Error Pattern

“不”字盲区——极其可惜的失分!将题干的”不正确”看成了”正确”,直接选了描述完全正确的组合。

Core Concept

  • 顺序表
  • 单链表
  • 存取特性

Expected Context

  • 笔记路径:顺序表与链表、栈与队列
  • 检索关键词:顺序表随机存取 O(1)、链表顺序存取 O(n)、存取特性、“不正确的是”审题、I/II/III/IV 多重判断

Fix Plan

考研极爱出带有 I、II、III、IV 的多重判断题。养成强迫症:

  1. 第一步,在题干的”正确/不正确”下面画双横线。
  2. 第二步,在 I、II 等选项后方打 √ 或 ×。
  3. 第三步,根据双横线的要求去捡 × 或者捡 √。

同类题预警:考研中”不正确的是”和”错误的是”等价于选描述错误的选项;而”正确的是”则选描述正确的。务必在题干的”不”字上画圈。此外,第 IV 条描述(顺序表查找时间与 i 无关)是正确的,不要因为直觉觉得”查找时间怎么可能无关”而打 ×。

变式自测:若题干改为”下面关于线性表的叙述中,正确的是()“,其他选项不变?(提示:题干从”不正确”改为”正确”,选正确描述项!答案:I、IV(D)。)


术语表

  • 数据结构:相互之间存在一种或多种特定关系的数据元素的集合。
  • 数据结构定义:数据结构的三要素包括逻辑结构、物理(存储)结构和数据的运算。
  • 线性表:由 n 个具有相同特性的数据元素组成的有限序列。
  • 顺序表:用一段地址连续的存储单元依次存储线性表的数据元素,支持随机存取。
  • 顺序存储结构:逻辑上相邻的元素在物理位置上也相邻的存储方式。
  • 随机存取:通过公式直接计算元素地址,可在 O(1) 时间内访问任意元素的能力。
  • 顺序存取:必须从头开始遍历才能访问指定元素,时间复杂度为 O(n)。
  • 头结点:在链表的第一个数据结点之前人为插入的占位结点,用于统一插入/删除操作。
  • 头指针:指向链表第一个结点的指针变量,用于标识链表的起始位置。
  • 单链表:通过指针链接的线性存储结构,每个结点包含数据域和指针域。
  • 单链表插入:在链表中插入新结点时需遵循”先连后断”的安全插入法则。
  • 指针操作:链表操作的核心,通过修改结点的 next 指针域来改变链表的逻辑结构。
  • 循环单链表:最后一个结点的指针域指向头结点(或第一个结点),形成环状结构。
  • 边界条件处理:处理链表操作时需考虑空表、唯一元素、头尾指针更新等特殊情况。
  • 时间复杂度:算法执行时间随数据规模增长的增长率,常用大 O 表示法。
  • 等比数列求和:用于计算内外层关联的嵌套循环的总执行次数。
  • 存取特性:顺序存储支持随机存取 O(1),链式存储仅支持顺序存取 O(n)。

相关链接


cd ..