简介:自考02331数据结构重点总结文档,是面向自考计算机专业考生的核心考点整理,覆盖数据构造的概论与线性表两大板块。文档从逻辑结构、存储结构、数据运算出发,梳理了顺序存储、链式存储、索引存储、散列存储四种基本方法,并总结了常见时间复杂度等级、算法评价准则,以及线性表顺序表与链表的初始化、查找、插入、删除等基础操作,重点突出、条理清晰。其中还包含顺序表地址计算、单链表头插法/尾插法建表、结点移动次数等易考易混细节,便于读者对照教材加深理解。资源为单个doc文档,文件总数为1,压缩包大小1.62MB,轻量易用,适合打印后逐章背诵或与教材搭配复习。目前已有97人学习下载,对准备自学考试、需要快速建立数据结构知识框架的读者而言,是一份值得收藏的考前冲刺资料。
1. 自考数据结构 02331:这份重点总结为什么值得当备考主线
数据结构是计算机专业的“分水岭”课程,自考 02331 更是卡住不少考生的一科。我拆完这份《自考数据结构重点总结最终.doc》后发现,它没有废话,从第一章概论到第五章树,覆盖了考试全部核心考点:算法复杂度、线性表、栈、队列、数组、广义表、二叉树,每个知识点都给出了结论和公式,省去了自己翻教材重新整理的时间。对准备自考、考研 408 或想补数据结构基础的程序员来说,这份文档可以直接当复习主线,配合教材查缺补漏即可。
文档定位是“应试导向的知识点总结”,不是入门教程,但足够精炼。它有两条主线:一是从逻辑结构、存储结构到算法评价指标的理论骨架,二是线性表、栈队列、树这几类核心结构的公式与操作细节。接下来,我按考试实际出题频率,把文档里的重点拆开讲,顺带补上文档没明说、但做题时绕不开的坑。
2. 算法复杂度与概论:先把“时间换空间”的账算明白
数据结构第一章常被考生跳过,但复杂度分析几乎出现在每套试卷的简答题或选择题里。文档里沃思那句“算法 + 数据结构 = 程序”是必考点,更要紧的是复杂度数量级排序——这是后面所有算法比较的基础。
2.1 五个准则与四种存储方法:选择题高频素材
算法必须满足输入、输出、有穷性、确定性、可行性五个准则。这里容易混淆的是“算法”与“程序”的差别:算法可以用自然语言或类 C 描述,不依赖具体编程语言;程序必须依赖计算机语言。这个点在判断题里反复出现,值得记牢。
四种存储方法各对应一种典型场景:顺序存储通常借助数组,适合随机访问;链式存储借助指针,适合频繁插入删除;索引存储通过关键字查找,适合静态查找表;散列存储直接由关键字计算地址,适合等值查找。选择题常给一个场景问“用哪种最合适”,记住“随机访问选顺序、频繁增删选链式”就够用。
2.2 时间复杂度递增序列:按数量级背下来
文档给出了完整的复杂度递增序列:常数阶 O(1)、对数阶 O(log₂n)、线性阶 O(n)、线性对数阶 O(nlog₂n)、平方阶 O(n²)、立方阶 O(n³)、k 次方阶 O(nᵏ)、指数阶 O(2ⁿ)、阶乘阶 O(n!)。
我备考时把这个序列画在纸上贴墙,每天扫一遍。题目给的代码块,先看是否有循环嵌套,再看循环变量是增还是减半。一层循环且变量增量为 1 是 O(n),两层嵌套是 O(n²),循环变量每次翻倍或减半是对数阶。
// 示例:对数阶写法 int i = 1; while (i < n) { i = i * 2; // 每次乘2,执行次数为 log₂n }执行次数取决于 i 每次乘 2,从 1 增长到超过 n 需要 log₂n 次。若把乘法改成加法,复杂度就退化成 O(n)。空间复杂度的定义类似,它包含算法本身占用空间、输入输出数据占用空间和运行时的临时空间,考试通常只问“额外辅助空间”。
3. 线性表:顺序表与链表的选择,本质是时间与空间的博弈
第二章是数据结构真正的核心,也是后续栈、队列实现的基础。文档里已经明确:顺序表是随机存取结构,插入删除平均要移动一半结点;链表靠修改指针完成插入删除,不用移动数据。这一章的考点集中在“移动次数计算”和“链表指针操作”两块。
3.1 顺序表插入与删除:移动次数必须手算过关
顺序表中第 i 个位置插入结点的移动次数是 n-i+1,删除第 i 个结点的移动次数是 n-i。很多同学把这两个公式记反,我提供一个记忆方法:插入多了一个空位,所以要多挪一个元素;删除直接补位,少挪一个。
平均移动次数:插入是 n/2,删除是 (n-1)/2,平均时间复杂度都是 O(n)。
// 顺序表插入操作(C语言) void InsertList(SeqList *L, int i, DataType x) { // L为顺序表指针,i为插入位置,x为待插入元素 int j; if (L->length >= MaxSize) { printf("表已满,无法插入\n"); return; } if (i < 1 || i > L->length + 1) { printf("插入位置不合法\n"); return; } for (j = L->length - 1; j >= i - 1; j--) { L->data[j + 1] = L->data[j]; // 从尾部开始逐个后移 } L->data[i - 1] = x; // 腾出的位置放入新元素 L->length++; }循环从最后一个元素开始向后移动,避免数据覆盖。位置从 1 计数,对应数组下标 i-1。判断表满条件、位置合法条件和插入后长度自增,这三处缺一不可。
3.2 头插法与尾插法:建表顺序的差异
头插法每次把新结点插到头结点之后,最终链表元素顺序与输入顺序相反;尾插法用尾指针记录当前末尾,顺序与输入一致。考试常给出输入序列,问建表结果,头插法的逆序特征是高频陷阱。
// 尾插法建带头结点的单链表(C语言) void CreateListR(LinkList *head, char input[]) { ListNode *r, *s; int i = 0; head = (ListNode *)malloc(sizeof(ListNode)); r = head; // 尾指针初值指向头结点 while (input[i] != '\0') { s = (ListNode *)malloc(sizeof(ListNode)); s->data = input[i]; // 生成新结点并给数据域赋值 r->next = s; // 新结点链到原表尾之后 r = s; // 尾指针指向新的表尾 i++; } r->next = NULL; // 终端结点指针域置空 }尾指针 r 始终指向链表的最后一个结点,每次新结点都链到 r 后面,然后 r 前移。三个建表算法的时间复杂度均为 O(n)。如果去掉头结点,第一个位置插入和删除就得单独处理,这是头结点存在的最大意义——统一空表与非空表的操作。
3.3 双向链表与单循环链表:指针修改顺序的先后逻辑
双向链表插入涉及四个指针修改,顺序不能乱。文档里的前插操作顺序是:先给新结点 s 的 prior 指向 p 的前驱、next 指向 p,再让 p 前驱的 next 指向 s,最后让 p 的 prior 指向 s。我先改 s 自身的两个指针,再改链上原有指针,顺序反了会丢失链信息。
单循环链表判断空的条件是 head == head->next,指向头结点自身。如果用尾指针 rear 表示单循环链表,连到 a₁ 和 an的时间都是 O(1),在做首尾插入删除时效率很高,这也是“约瑟夫环”类题目优先选尾指针循环链表的原因。
4. 栈与队列:受限线性表的两张规则牌
栈是 LIFO,队列是 FIFO,本质都是“受限的线性表”。自考第三章的高频考点集中在循环队列的判空判满,以及后缀表达式转换。文档里对“上溢”和“下溢”的区分是基础题常客:上溢是栈满再做入栈,为错误状态;下溢是栈空再做退栈,常被用作控制转移条件。
4.1 栈的顺序存储:栈顶指针指向栈顶元素
顺序栈用数组模拟,栈顶指针 top 指向栈顶元素。入栈时先把 top 加 1,再存入元素;出栈时先取元素,再把 top 减 1。空栈时 top 为 -1,这是文档里特别标注的:空栈时栈顶指针不能是 0。两个栈共享空间时,栈底设在两端,top1 和 top2 相向增长,栈满条件为 top1 == top2 - 1。
4.2 循环队列的判空判满:三种方案的边界条件
顺序队列有“假溢出”问题——front 前移释放的空间不能再利用。循环队列通过模运算把数组首尾相连,入队、出队都按 (i+1) % QueueSize 推进。但这也带来一个新问题:队空和队满时 front 都等于 rear,必须另加判断。
文档给出三种解法,考试常考的是第三种“少用一个元素空间”:队满条件为 (Q->rear + 1) % QueueSize == Q->front,队空条件为 Q->rear == Q->front。此时 rear 所指单元始终为空,队列实际最多存 QueueSize-1 个元素。
// 循环队列基本操作(C语言) #define QueueSize 100 typedef struct { DataType data[QueueSize]; int front, rear; } CirQueue; // 入队 int EnQueue(CirQueue *Q, DataType x) { if ((Q->rear + 1) % QueueSize == Q->front) { return 0; // 队列满,返回0表示失败 } Q->data[Q->rear] = x; Q->rear = (Q->rear + 1) % QueueSize; // 循环意义下尾指针加1 return 1; } // 出队 DataType DeQueue(CirQueue *Q) { DataType temp; if (Q->rear == Q->front) { return NULL; // 队列空 } temp = Q->data[Q->front]; Q->front = (Q->front + 1) % QueueSize; // 循环意义下头指针加1 return temp; }队满判断里用取模运算把 rear 推进到数组末端时折回开头,这样数组空间可以反复使用。记住一个口诀:入队先判满、出队先判空、指针移动都要取模。
4.3 链栈与链队列:无需判满的内存动态分配
链栈本质是不带头结点的单链表,栈顶指针就是链表头指针。链栈结点动态分配,理论上只要有内存就不会上溢,所以不需要定义 StackFull 运算。链队列为方便处理,在队头前附加头结点,入队只改尾指针,出队只改头指针。当原队列只有一个结点时,出队要同时修改头尾指针,删完后队列变空。
还有一个高频简答题:中缀表达式如何转后缀表达式。核心是操作数直接输出,运算符入栈时比较优先级,右括号弹出栈顶直到左括号。文档在第三章结尾简单提了这个问题,考试卷上会配合栈的操作步骤来考。
5. 数组、广义表与二叉树:公式与性质的记忆工程
第四章和第五章内容多、公式多,是考卷里计算题和证明题的主阵地。文档里给出的所有地址计算公式和二叉树性质都属于“必须会背且能推导”的硬功夫。
5.1 数组地址计算:下界为 1 与下界为 0 的区分
二维数组按行优先存储的地址公式为 LOC(aᵢⱼ) = LOC(a₁₁) + [(i-1)×n + j-1]×d,其中 n 是每行元素个数,d 是单个元素所占存储单元。若数组下标从 0 开始,公式简化为 i×n+j。按列优先时行数和列数互换。
三维数组的地址公式在文档里也有:LOC(aᵢⱼₖ) = LOC(a₁₁₁) + [(i-1)×n×p + (j-1)×p + k-1]×d。做题时先确认下界是 0 还是 1,这直接决定是否要减 1。我在这里翻过车——把下界为 1 的三维数组套用了二维公式,结果偏差很大,后来我每次做题都先圈出题目里的下标范围,再写公式。
5.2 对称矩阵与三角矩阵压缩:一维数组下标映射
对称矩阵只需存下三角或上三角,矩阵元素 aᵢⱼ 与一维数组下标 k 的映射关系要记牢。下三角按行优先存时,k = i×(i+1)/2 + j(i≥j)。我推荐用“等差数列求和”来理解:第 i 行前面共有 i 行,每行元素数从 1 递增到 i,前 i 行元素总数为 i×(i+1)/2,再加上当前行的列偏移 j。
三角矩阵压缩时,重复元素常量 c 存到一维数组最后一个位置,其余 n×(n+1)/2 个元素按规则顺序存放。这类题计算量不大,关键是把 i 和 j 谁大谁小判断准。
5.3 稀疏矩阵的三元组表:失去随机存取功能
稀疏矩阵只存非零元素,用三元组 (i, j, aᵢⱼ) 记录行列位置和值,这是顺序存储方案;十字链表是链式存储方案。要特别注意文档里的结论:稀疏矩阵压缩后“失去随机存取功能”——因为无法通过行列号直接算出存储位置,必须逐个查找。
5.4 二叉树四个性质:n₀ = n₂ + 1 是证明题最爱
二叉树性质 1 到性质 4 中,性质 3(终端结点数 n₀ = 度为 2 的结点数 n₂ + 1)考试频率最高,不但会直接考,还常用来做推导题。理解方式:从叶子数角度出发,每增加一个分支结点,叶子数不增加,但度为 2 的结点数加 1 时,叶子数也加 1,所以叶子数总比度为 2 的结点数多 1。
性质 4 的深度计算公式是 ⌊log₂n⌋ + 1 或 ⌈log₂(n+1)⌉。文档举例:100 个结点的完全二叉树深度为 ⌊log₂100⌋ + 1 = 6 + 1 = 7,因为 2⁶ = 64,2⁷ = 128。
5.5 完全二叉树编号:双亲与孩子编号的推导
编号从 0 开始时,结点 i 的双亲编号为 ⌊(i-1)/2⌋,左孩子编号为 2i+1,右孩子编号为 2i+2。这里有个需要避开的坑:文档中同时给出了编号从 1 开始和从 0 开始的两套规则,考试必须看清题目给的是哪套。从 0 开始时,左孩子存在条件是 2i+1 < n;从 1 开始则是 2i ≤ n。
完全二叉树适合顺序存储,因为编号能直接映射到数组下标。但一般二叉树用顺序存储要补虚结点,存储密度低,所以通常用二叉链表。
5.6 二叉链表与前中后序遍历:唯一确定二叉树的条件
n 个结点的二叉链表共有 2n 个指针域,其中 n-1 个指向孩子,n+1 个为空。线索二叉树利用这些空指针域存放前驱后继指针,一个结点是叶结点的充要条件是左右标志均为 1。
遍历的核心是递归。前序:访问根→左子树→右子树;中序:左子树→访问根→右子树;后序:左子树→右子树→访问根。确定二叉树只需“前序 + 中序”或“后序 + 中序”,前序 + 后序不能唯一确定。解题方法是先用前序或后序确定根,再用中序把左右子树切开,递归进行。
6. 避坑与考前强化:五个高频翻车点 + 验证方法
这份文档我前前后后拆了三遍,发现内容本身没有硬伤,但考生在应用阶段最容易在几个固定位置翻车。下面把常见问题按“现象 → 原因 → 解决”列出来。
6.1 复杂度化简出错:把 O(2n) 写成 O(2)
现象:学生在计算一层循环的时间复杂度时写成 O(2) 或 O(2n)。
原因:复杂度关注数量级,系数和常数项在渐进分析中丢弃。
解决:算完执行次数后直接看最高阶项,去掉系数。O(2n) 化简为 O(n),O(n²+n) 化简为 O(n²)。做题时保留系数不化简,会被判概念错误。
6.2 顺序表删除移动次数与插入记混
现象:删除第 i 个元素写成 n-i+1。
原因:只背了“插入公式”,忽略了插入和删除的移动起点不同。
解决:用模拟法验证一次。插入时第 i 个位置本身空着,从第 i 个到第 n 个都要后移,共 n-i+1 个;删除时第 i 个直接移除,后面 n-i 个前移。记住“删除少移一位”。
6.3 循环队列判空判满方案混用
现象:用 Q.rear == Q.front 同时判断空和满,导致入队错误。
原因:只记住一个条件,没有区分方案的适用前提。
解决:凡是用“少用一个元素空间”方案,判满必须是 (Q.rear+1) % QueueSize == Q.front;判空才是 Q.rear == Q.front。如果看到题目有“计数器”或“标志位”,再用对应的方案。
6.4 数组地址计算忽略下界条件
现象:题目说数组下标从 0 开始,仍套用减 1 的行优先公式。
原因:教材默认公式下界为 1,但试卷常改为下界 0。
解决:提笔前先用笔圈出“下标从 0 开始”或“从 1 开始”。从 0 开始时行优先公式变为 i×n+j,不减 1。三元组里行号和列号也要对齐这个规则。
6.5 广义表表头表尾理解偏差
现象:题目问 tail((a,b)) 的结果,答成 a。
原因:把表尾理解成“最后一个元素”,而定义是“除表头外其余元素组成的子表”。
解决:任何非空广义表的表尾一定是子表。所以 tail((a,b)) = (b),不是 b;head((a,b)) = a,是原子。广义表 () 是空表,不能求表头和表尾;而 (()) 长度为 1,表头和表尾都是空表 ()。
验证自己是否掌握这份文档,我用过最有效的方法是:合上文档,在白纸上把复杂度递增序列、顺序表插入删除移动次数公式、循环队列判空判满条件、二叉树四个性质和三类遍历顺序各写一遍,然后对照原文。写不出来的地方就是漏洞。
从那以后,我每次考前复习数据结构,都强制自己先过一遍这五类考点——我觉得这份文档最值钱的地方就是把散落在教材各章节的计算公式和边界条件汇总到了一起。文档是死的,但考试用得上;如果你能把上面的公式条件练到闭眼默写,那这张卷子对你来说就是一场计算的重复劳动。希望帮到你。
本文还有配套的精品资源,点击获取