
简介数据结构考研总结PDF超清版面向计算机专业考研及期末复习人群系统梳理数据结构基础知识点与核心考点。内容从数据、数据元素、数据对象等基本概念入手涵盖逻辑结构与存储结构分类、抽象数据类型ADT定义格式、算法特性与评价标准并重点展开线性表和顺序表的定义、实现及基本操作配有静态分配与动态分配的代码示例便于对照理解。资源为1个PDF文件压缩包约1.19MB排版清晰、内容精炼可直接打印或导入笔记软件使用适合冲刺阶段快速回顾和搭建知识框架。已有924人学习下载是一份轻量但覆盖全面的数据结构备考资料。1. 数据结构考研总结一份能把零散考点背进考场的PDF你要是真在准备考研数据结构大概率经历过这种状态概念背得滚瓜烂熟一上手写顺序表插入就忘了边界条件是ji还是jiKMP也背了next数组一推就乱。这份总结PDF就是冲着这个痛点来的——从绪论里的基本术语、三要素、四种存储结构到线性表、栈、队列、串、树一路整理到哈夫曼编码公式和代码都集中在几十页里。对复习时间紧的选手它更像一本考前背诵手册白天看王道和《大话数据结构》把原理弄懂晚上直接拿这份PDF过一遍手写不出来的地方标红第二天重点补。适合考研数据结构、408考生以及被期末复习按在地上摩擦的本科生。2. 顺序表与单链表实现细节和四个易错点2.1 顺序表静态分配与动态分配怎么选顺序表的本质是“逻辑相邻 物理相邻”一维数组实现地址计算公式是(A[0]) i * sizeof(ElemType)。这份总结里给了两种分配方式考试手写代码时二选一即可。#define MaxSize 50 // 定义线性表的最大长度 typedef struct{ ElemType data[MaxSize]; // 顺序表的元素 int length; // 顺序表的当前长度 }SqList; // 顺序表的类型定义静态分配的问题很明显空间事先固定一旦占满再插入就溢出程序直接崩。所以很多学校的数据结构实验报告和考研题偏爱动态分配用malloc或new在堆上开空间满了就换一块更大的。#define InitSize 100 // 表长度的初始定义 typedef struct{ ElemType *data; // 指示动态分配数组的指针 int MaxSize,length; // 数组的最大容量和当前个数 }SqList; // 动态分配数组的顺序表类型定义 L.data (ElemType*)malloc(sizeof(ElemType)*InitSize); free(L);C 和 C 的写法不同但语义一样malloc配freenew配delete混用会出玄学内存错误。我一般建议考生手写时用malloc因为王道和天勤的参考代码都是这套阅卷时看着亲切。注意ElemType是抽象类型考试时可以直接替换成int但结构体定义里的大小写必须全文统一这份PDF里有几处Elemtype和ElemType混排你抄代码时统一成一种就行。2.2 插入、删除、按值查找边界条件才是拿分点顺序表的核心操作就三个插入、删除、按值查找。代码不长但坑全藏在边界里。bool ListInsert(SqList L, int i, ElemType e){ if(i1 || iL.length1) return false; // 插入位置有效范围 [1, length1] if(L.lengthMaxSize) return false; // 存储空间已满 for(int jL.length; ji; j--) L.data[j] L.data[j-1]; // 从后往前逐个后移 L.data[i-1] e; // 位序 i 对应数组下标 i-1 L.length; return true; } bool ListDelete(SqList L, int i, ElemType e){ if(i1 || iL.length) return false; // 删除位置有效范围 [1, length] e L.data[i-1]; // 先取被删元素 for(int ji; jL.length; j) L.data[j-1] L.data[j]; // 从前往后逐个前移 L.length--; return true; } int LocateElem(SqList L, ElemType e){ for(int i0; iL.length; i) if(L.data[i] e) return i1; // 返回值是位序不是下标 return 0; }三个操作各有一个典型翻车点。插入时移动元素必须从最后一个开始倒着移正着移会把后面的元素覆盖掉删除相反要从被删元素的下一个开始正着移。按值查找返回的是位序i1不是下标i很多人栽在这里因为题目问你“第几个元素”而不是“数组第几位”。还有这份PDF里插入函数名写成了ListInset少个r照抄会编译报错上面代码我已改正。2.3 单链表头插法、尾插法和头结点的作用链表和顺序表的最大区别是“逻辑相邻但物理不一定相邻”靠指针维系关系。单链表结点定义固定如下LinkList本质是LNode*的别名。typedef struct LNode{ ElemType data; // 数据域 struct LNode *next; // 指针域 }LNode, *LinkList;建表两种方式必须都熟。头插法每次把新结点插在头结点后面最后得到的链表是逆序的尾插法需要一个尾指针r始终指向最后一个结点保持输入顺序。LinkList List_HeadInsert(LinkList L){ LNode *s; int x; L (LinkList)malloc(sizeof(LNode)); L-next NULL; // 初始空链表 scanf(%d, x); while(x ! 9999){ s (LNode*)malloc(sizeof(LNode)); s-data x; s-next L-next; // 新结点指向原首元结点 L-next s; // 头结点指向新结点 scanf(%d, x); } return L; }LinkList List_TailInsert(LinkList L){ int x; L (LinkList)malloc(sizeof(LNode)); LNode *s, *r L; // r 为表尾指针 scanf(%d, x); while(x ! 9999){ s (LNode*)malloc(sizeof(LNode)); s-data x; r-next s; // 接到表尾 r s; // 尾指针后移 scanf(%d, x); } r-next NULL; // 尾结点指针置空 return L; }为什么几乎所有的链表题都带头结点两个原因一是对首元结点的操作和其他结点统一了不用单写if(pL)分支二是空表和非空表的处理统一头指针永远不空。按值查找就是从头结点的下一个开始遍历找到返回结点指针找不到返回NULL这个代码简单但必须会默写。顺序表和单链表的取舍是常考简答题顺序表随机存取、存储密度高但插入删除要移动大量元素、静态分配会留碎片链表插入删除只需改指针、空间零散利用但只能顺序存取、每个结点要多花一个指针域的空间。408选择题和期末考都爱在这个对比上出题。2.4 双链表、循环链表和静态链表知道结构差异就够双链表在单链表基础上加一个prior指针指向前驱删除结点时要同时改两个方向的指针插入删除的指针操作比单链表多两步最容易写漏的是忘记把新结点的prior指向正确位置。循环单链表和循环双链表的判断条件从pNULL变成pL遍历终止条件别写错。静态链表是用数组模拟链表指针域存的是数组下标游标理解为一个“用数组实现的链式结构”考试一般只考结构定义和查找思路不要求完整实现。3. 栈和队列手写代码的拿分点与循环队列入门3.1 顺序栈和共享栈top指针的三个细节栈是操作受限的线性表只在一端插入删除LIFO。顺序栈定义里有三个关键约定S.top -1是栈空S.top MaxSize-1是栈满S.top1是栈长。进栈是top先加 1 再赋值出栈是先取值再top减 1方向和顺序表完全相反别记反了。#define MaxSize 50 typedef struct{ ElemType data[MaxSize]; // 存放栈中元素 int top; // 栈顶指针 }SqStack;共享栈是顺序栈的省钱版本两个栈底固定在数组两端栈顶往中间延伸。top0 -1时 0 号栈空top1 MaxSize时 1 号栈空top1 - top0 1时栈满。0 号栈进栈top0先加 11 号栈进栈top1先减 1。这种结构把“栈满上溢”的概率降到最低因为两个栈互补着用空间。链栈没有头结点Lhead直接指向栈顶元素好处是不会栈满缺点是每个结点多一个指针域这篇总结里给了链栈的结点定义但没展开实现考试默写时记得push和pop都要处理Lhead的更新。3.2 循环队列判空判满的三种方案队列的队头指针front指向队头元素队尾指针rear指向队尾元素的下一个位置这个约定和有些教材不同先确认你考试用的哪种。顺序队列最大的问题是假溢出——rear到数组末尾但前面还有空位。循环队列用取余运算让指针绕回 0Q.front (Q.front1) % MaxSizeQ.rear (Q.rear1) % MaxSize队列长度是(Q.rear MaxSize - Q.front) % MaxSize。循环队列的判空判满有历史包袱初始状态front rear 0是空队可如果一直入队rear追上front时也是front rear不做区分就没法判断。总结里给了三种方案408真题都考过方案队空条件队满条件队列长度代价牺牲一个存储单元Q.front Q.rear(Q.rear1) % MaxSize Q.front(Q.rear - Q.front MaxSize) % MaxSize少用一个格子增设 size 成员Q.size 0Q.size MaxSizeQ.size每次增删要维护 size增设 tag 成员tag0且frontreartag1且frontrear无直接公式靠最后一次操作是入队还是出队区分方案一是考研默认写法因为不用改结构体定义只改逻辑。大小写敏感手写时注意MaxSize在结构体定义和取余表达式里必须一致有人把%MaxSize写成%MAXSIZE导致编译不过这种低级错误在考场上最冤。3.3 括号匹配和后缀表达式栈应用的代码套路栈在括号匹配里的套路顺序读入括号左括号压栈右括号和栈顶比对。如果读到右括号时栈是空的说明右括号多了最后扫描完栈还不空说明左括号多了。判断条件就两个栈空不空、配不配。中缀转后缀的核心逻辑是“先算的运算符后入栈”遇到右括号就把栈里的运算符弹到后缀表达式里弹到左括号为止遇到普通运算符时优先级不高于栈顶就弹栈顶。后缀表达式求值更简单数字压栈遇到运算符弹两个操作数算完再压回去这里注意弹栈顺序——先弹出的是 Y后弹出的是 X运算要按X op Y算顺序反了减法和除法直接算错。这个坑在期末复习题里出现率极高不是不会是弹栈顺序下意识写反了。4. KMP避坑指南next推导、nextval修正与三个常见翻车现场4.1 前缀后缀与部分匹配值先弄清楚PM是什么KMP 的整个理论地基就一句话主串指针不回溯模式串指针根据 next 数组跳转。而 next 数组的源头是部分匹配值 PM——字符串的前缀和后缀的最长相等前后缀长度。注意前缀是“除最后一个字符外的所有头部子串”后缀是“除第一个字符外的所有尾部子串”两个都必须是真前缀真后缀不能包含整个字符串本身。以abcac为例编号12345S 字符abcacPM 部分匹配值00010第 4 个字符a的 PM 是 1因为前缀a和后缀a相等长度是 1。整个串abcac的最长相等前后缀不是ac前后缀都不能跨过最后一个字符这是初学最容易想歪的地方。4.2 next数组三步推导法PM右移再加一教材里 next 数组的推导公式绕得人头疼这篇总结给了一个可操作的口诀PM 右移一位空出的首位补 -1然后整体加 1。编号12345S 字符abcacPM 部分匹配值00010nextPM右移一位-10001next next 101112得到next [0,1,1,1,2]。注意下标从 1 开始计数时next[1]固定为 0表示“第一个字符都失配了主串指针要后移一位”。失配时模式串指针跳转的公式是j next[j]比如第 5 个字符c失配就跳到第 2 个字符重新比较。网上很多博客用 0 基数组把整个推导绕进死胡同我强烈建议你按“1 基数组 0 表示空”来记王道和张宇的讲义都是这么处理的考试答题也最不容易错。有匹配失败反复回溯的场景KMP 就比 BF 香了BF 最坏时间复杂度 O(n*m)KMP 是 O(nm)。主串越长、失配越早差距越明显。408 选择题爱问“主串指针回溯几次”“模式串指针跳到第几”用这套 next 推导可以直接手算不需要真的跑代码。4.3 nextval修正把多余的比较直接跳掉next 数组有个特殊情况如果next[j]指向的字符和P[j]本身相同那这次跳转注定还要失配不如直接跳到next[next[j]]这就是 nextval。规则一句话失配跳转目标位置的字符与当前字符相同就用目标位置的 nextval 替代当前 next 值。手算时先求 next再从左到右逐位检查如果P[j] P[next[j]]nextval[j] nextval[next[j]]否则nextval[j] next[j]。这是 KMP 的优化版本很多 408 应用题直接让你求 nextval这一分是白捡的。4.4 避坑记录四个KMP翻车现场翻车一next[1] 写成 1 导致死循环。现象手写 KMP 匹配时程序卡死或者 ”while(j0 P[j]!T[i])“ 跳不出去。 原因模式串第一个字符失配时正确的做法是主串指针后移、模式串回到 1即 next[1]0 作为哨兵。有人受部分匹配值影响写出 next[1]1失配时 j 永远跳不回 0。 解决背死规则——next[1] 恒为 0next[2] 恒为 1除单字符模式串外。推导时按下标从 1 开始的“PM 右移 1”流程别用 0 基数组硬套。翻车二PM 右移时补位补成 0。现象推出来的 next 数组第一位是 0看起来和正确情况一样但第三四位顺次对不上。 原因很多人记“PM右移一位前面补0”补进去的 0 参与整体加 1 变成 1而正确做法是补 -1 再加 1 得 0。 解决记住 -1 是“主串要动”的标记0 是“模式串从头开始”的标记两者语义不同。推导表上先写一行 PM下面是右移行空位写 -1最后整体加 1。翻车三写 KMP 匹配时让主串指针回溯。现象代码能跑通但和 BF 的复杂度一样O(n*m)KMP 白学了。 原因KMP 的精髓是主串指针 i 永不回溯失配时只动 j。有人写i i - (j - next[j])试图“优化”实际把 KMP 退化成带跳跃的 BF。 解决失配处理只写j next[j]当j 0时i、j。主串遍历用while(i n)别在循环里改 i 的失配分支。这段血泪经验是我刷王道课后题时一晚上踩齐的全部是“代码不长逻辑差一点就全错”的典型。把这四条抄在 PDF 那一页的边上考前十分钟只看这几条就够。5. 树与二叉树线索化、BST删除与哈夫曼WPL验算5.1 线索二叉树空指针不浪费直接指前驱后继n 个结点的二叉树用二叉链表存必有 n1 个空链域。线索化就是把这 n1 个空指针利用起来左孩子为空就指前驱右孩子为空就指后继同时用ltag和rtag标记“这个指针到底是指孩子还是指线索”0 指孩子、1 指线索。中序线索二叉树找后继的规则最顺右标记为 1 就右链即后继否则右子树第一个访问的结点是后继。后序线索化找后继需要三叉链表因为后序遍历里后继可能是双亲。5.2 BST删除和平衡旋转真题里的固定组合BST 删除分三种情况叶子直接删只有一棵子树就“让孩子的上位”有两棵子树时用中序直接后继替代被删结点再删掉那个直接后继。平衡旋转四种——LL 右单旋、RR 左单旋、LR 先左后右、RL 先右后左——判断方法看出问题的结点在“左孩子的左子树”还是“左孩子的右子树”。手画平衡树时先标失衡结点再按形状选旋转删除题做到这步基本就是固定套路了。5.3 哈夫曼构造与WPL验算不重不漏的最后一道关哈夫曼树的构造步骤是死的n 个结点看作 n 棵单结点树不断选权值最小的两棵合并新结点权值是两者之和。最终树结点总数是 2n-1且不存在度为 1 的结点。我一般会用“新结点逐轮累加”来验算每合并一次把新结点权值加入一个累加器最终累加值就是 WPL同时数一下总结点数是否是 2n-1两个条件同时满足构造基本不会错。从那以后我每次复习都强制自己手推一张哈夫曼编码表、验算一遍WPL再默写一次中序线索化的后继查找规则这三个动作花不了十分钟但考场上都是直接拿分的题。这份PDF把表达式求值、KMP、线索二叉树这些最容易“看着懂、写不出”的内容全部浓缩在一起适合考前两周用来做查漏清单。希望帮到你。本文还有配套的精品资源点击获取