06 / 算法与线性结构:让步骤可执行 · 小节 1
算法、复杂度与递归
用输入输出、终止条件和不变式解释一个算法。
大纲要求的教学展开 · 技术讲解 · 本节来源与考点
前置知识:数组、指针与结构体怎样配合 让计算机按顺序做事 · 符号回看
从一个问题开始
找出一摞书中编号最大的书,为什么“多试几次”不算完整算法?
调用向下,结果向上
fact(3)等待3×fact(2)。
规模逐次减少,到fact(0)=1停止展开。
由内向外返回:1、2、6;调用状态逐层释放。
原创教学示意 · 手动播放,可随时暂停;折叠或离开小节时停止。
把这件事讲清楚
算法是解决一类问题的有限、明确、可执行步骤,须说明输入、输出和适用条件。伪代码表达逻辑,不依赖具体编译器。本课程过程示例除明确列出的C文件外均为伪代码。O(n)描述规模增长时工作量的上界阶,不是精确秒数。常见阶为常数、对数、线性、n log n、平方。额外空间只计辅助存储还是含输入,应先说明。
递归是函数调用自身,必须有基本情形和趋近它的度量。每层调用保留局部状态,深度会占栈空间,递归不会天然更快。
为什么成立 · 关键推导
求最大值:若数组空则返回“无最大值”;否则max←a[0],从i=1到n−1比较并更新。循环不变式:处理完下标i后,max是前i+1项最大值。递归阶乘:fact(0)=1;n>0时fact(n)=n×fact(n−1),n必须非负且结果未溢出。
入门例题 / 1
[3,1,4,4]依次比较,max为3→3→4→4,3次比较,时间O(n)、额外空间O(1)。重复最大值不影响值结果,但求“全部位置”要另存索引。
典型应用与变式 / 2
fact(3)展开3×fact(2)→3×2×fact(1)→3×2×1×fact(0),再回收得6。时间O(n),调用栈O(n);迭代累乘额外空间可为O(1)。
停一下,自己试一试
先独立作答;卡住时看提示,完成后再对照解析。
概念判断
O(n²)是否表示每个输入都恰好执行n²步?
需要一点提示
阶忽略常数与低阶项。
查看过程、答案与错因
不是,也可能是3n²+2n等上界,还须说明最好/平均/最坏情形。
计算与执行过程
双循环i=1到n,内层j=1到i,执行次数?
需要一点提示
求1+2+…+n。
查看过程、答案与错因
n(n+1)/2,量级O(n²)。不能因为内层上界变化就说O(n)。
条件与错误辨析
递归f(n)=f(n)为什么无法正常结束?
需要一点提示
度量是否减少?
查看过程、答案与错因
没有更小子问题,也没有能到达的终止分支,会反复调用直至资源耗尽。
继续实践 · 工具与示范
先按图示和正文用纸笔追踪,再阅读以下伪代码。箭头←为赋值,“若”为条件,“当”为循环;不是可直接编译的C。
max(a,n):
若 n=0:返回“无最大值”
best ← a[0]
对 i=1 到 n−1:
若 a[i]>best:best ← a[i]
返回 best带走这一句
算法要同时回答:算什么、为何对、何时停、要多少资源。
06 / 算法与线性结构:让步骤可执行 · 小节 2
顺序表与链表:搬元素还是改指针
逐步执行两种存储结构的插删,选择合适实现。
大纲要求的教学展开 · 技术讲解 · 本节来源与考点
从一个问题开始
队伍中间插一个人,数组和链表为什么付出的工作不同?
插入节点:先接后继,再接前驱
原链表A→B→C,新节点S尚未接入。
先令S.next指向B,原链不断开。
再令A.next指向S,形成A→S→B→C。
原创教学示意 · 手动播放,可随时暂停;折叠或离开小节时停止。
把这件事讲清楚
线性表是有序元素序列的逻辑结构;顺序表连续存储,按下标O(1)访问,中间插删需移动;链表通过指针连接,查第i个一般O(n)。已知前驱节点时,单链表插删可O(1),但寻找位置的时间不能漏算。链表额外保存指针,缓存局部性通常不如数组。带头节点可统一首元素操作,头节点不必存业务数据。
为什么成立 · 关键推导
数组位置i插入v:检查容量→从尾向右搬移→a[i]←v→长度加1。链表在p后插入s:先s.next←p.next,再p.next←s,顺序反了会丢原后继。删除p后q:保存q←p.next→p.next←q.next→释放q。需检查q存在。
入门例题 / 1
[2,5,8]在下标1插4:先8右移,再5右移,写4,得[2,4,5,8]。若从左往右移会覆盖未保存的数据。
典型应用与变式 / 2
A→B→C在A后插S:先S→B,再A→S,得到A→S→B→C。删除B须先找到其前驱S;只持有B地址时不能一般性地声称O(1)完成所有单链表删除情形。
停一下,自己试一试
先独立作答;卡住时看提示,完成后再对照解析。
概念判断
链表按下标访问一定比数组快吗?
需要一点提示
能否直接算第i项地址?
查看过程、答案与错因
不能。数组通常O(1),单链表需沿next走,最坏O(n)。
计算与执行过程
A→B→C删除B,写出修改的指针。
需要一点提示
先定位前驱A。
查看过程、答案与错因
q←A.next即B;A.next←q.next即C;释放q。A→C,不能先释放B再读B.next。
条件与错误辨析
空表删除首元素要检查什么?
需要一点提示
首节点是否存在?
查看过程、答案与错因
检查头指针或头节点next不为空;不存在则返回失败/空结果,不能解引用NULL。
继续实践 · 工具与示范
先按图示和正文用纸笔追踪,再阅读以下伪代码。箭头←为赋值,“若”为条件,“当”为循环;不是可直接编译的C。
insert_after(p,s):
要求 p,s 有效且 s 不在链中
s.next ← p.next
p.next ← s
remove_after(p):
q ← p.next
若 q 为空:返回失败
p.next ← q.next
保存结果,再释放由本表拥有的 q带走这一句
选择结构时,把“找到位置”和“修改位置”分开算。
06 / 算法与线性结构:让步骤可执行 · 小节 3
栈与队列:不同的出场顺序
手工模拟入栈出栈和队列,理解它们在程序中的用途。
大纲要求的教学展开 · 技术讲解 · 本节来源与考点
前置知识:顺序表与链表:搬元素还是改指针 · 符号回看
从一个问题开始
撤销最后一次操作与按到达顺序叫号,能用同一种出队规则吗?
先进先出的队列
入A、B:队头A先到。
出队得到A,B成为队头。
再入C,后续依次出B、C。
原创教学示意 · 手动播放,可随时暂停;折叠或离开小节时停止。
把这件事讲清楚
栈后进先出LIFO,常用于递归、括号匹配、撤销;队列先进先出FIFO,用于排队和广度优先。栈只有栈顶可直接进出。循环队列用取模让下标绕回:next=(index+1)%capacity。若预留一格区分空满,空条件front=rear,满条件(rear+1)%capacity=front,实际容量比数组长度少1;也可另设计数,但条件应随实现改变。
为什么成立 · 关键推导
括号匹配:遇左括号压栈;遇右括号先检查栈非空且顶类型匹配,再弹出;结束时栈必须空。队列入队写rear后前移,出队读front后前移。每步先判断满/空。
入门例题 / 1
依次压入1、2,弹出得到2;再压3,弹出3、1。入栈序列保持,出栈序列受中间弹出时机影响。
典型应用与变式 / 2
长度5预留一格,front=3,rear=1,元素数(1−3+5)%5=3,位置3、4、0有效;再入队写位置1,rear变2,此时(2+1)%5=3等于front,队满。
停一下,自己试一试
先独立作答;卡住时看提示,完成后再对照解析。
概念判断
“([)]”左右括号数量相同就匹配吗?
需要一点提示
栈顶要求就近配对。
查看过程、答案与错因
不匹配。读到)时栈顶是[,类型不符。计数不能检测嵌套顺序。
计算与执行过程
队列入A、B,出一个,再入C,后续出队顺序?
需要一点提示
先进先出。
查看过程、答案与错因
第一次出A,余B;入C后依次B、C。
条件与错误辨析
预留一格的长4循环队列最多存4项吗?
需要一点提示
空满判定占了一格。
查看过程、答案与错因
最多3项;若要存4项须换用计数或满标志,并相应修改条件。
继续实践 · 工具与示范
先按图示和正文用纸笔追踪,再阅读以下伪代码。箭头←为赋值,“若”为条件,“当”为循环;不是可直接编译的C。
match(text):
栈 S ← 空
逐字符 c:
若 c 为左括号:push(S,c)
若 c 为右括号:
若 S 为空或栈顶类型不匹配:返回假
pop(S)
返回 S 为空带走这一句
栈关心最近一步,队列关心最早到达。
06 / 算法与线性结构:让步骤可执行 · 小节 4
串、数组与稀疏矩阵
看懂字符串匹配过程和节省存储的条件。
大纲要求的教学展开 · 技术讲解 · 本节来源与考点
前置知识:栈与队列:不同的出场顺序 · 符号回看
从一个问题开始
一个百万行矩阵只有千个非零数,仍要把所有零存下来吗?
把这件事讲清楚
串是字符序列,子串连续,子序列可跳过。朴素模式匹配在每个起点逐字符比较,最坏O(nm)。KMP利用模式自身前后缀信息,失配时保留已知匹配关系,时间O(n+m),不必把主串指针退回。不同next或前缀函数定义有不同下标约定,必须先写定义。
二维数组按行优先时,a[i][j]偏移为(i×列数+j)×元素大小。稀疏矩阵可按(行,列,值)三元组存非零项,另存尺寸与数量;适合零很多但索引元数据也有成本。
为什么成立 · 关键推导
定义前缀函数π[i]为模式0…i的最长相等真前缀/后缀长度,“真”表示不能用整个串。模式ABAB的π=[0,0,1,2]。若已匹配ABAB后失配,可保留长度2的AB,从较短候选继续,不重查主串前两位。
入门例题 / 1
主串ABABC、模式ABC。朴素在0匹配AB后遇A≠C失败,起点1首字符B失败,起点2匹配成功。0起始返回2。
典型应用与变式 / 2
3×4按行数组,每项4字节,a[2][1]偏移(2×4+1)×4=36字节。矩阵只存(0,1,5)、(1,2,7)及2×3尺寸,转置时交换行列并按需要重新排序。
停一下,自己试一试
先独立作答;卡住时看提示,完成后再对照解析。
概念判断
“AC”是“ABC”的子串吗?
需要一点提示
是否连续?
查看过程、答案与错因
不是子串,是子序列。模式匹配题若没说子序列,不能跳过B。
计算与执行过程
求AAAA的前缀函数π。
需要一点提示
每个前缀排除自身。
查看过程、答案与错因
[0,1,2,3];最后最长真前后缀AAA长3。
条件与错误辨析
所有矩阵都适合三元组存储吗?
需要一点提示
每项还保存两个坐标。
查看过程、答案与错因
不适合。稠密矩阵可能因坐标开销更大且访问更慢,应根据非零比例和操作模式选择。
继续实践 · 工具与示范
先按图示和正文用纸笔追踪,再阅读以下伪代码。箭头←为赋值,“若”为条件,“当”为循环;不是可直接编译的C。
prefix(pattern):
若模式空:返回空表
pi[0] ← 0
对 i=1 到 m−1:
j ← pi[i−1]
当 j>0 且 pattern[i]≠pattern[j]:j ← pi[j−1]
若 pattern[i]=pattern[j]:j ← j+1
pi[i] ← j
返回 pi
空模式在查找中的结果须另定,本课约定匹配位置0。带走这一句
表示法服务于操作;少存数据并不自动更快。
章末 / 把知识接起来
综合训练
原创综合训练
为括号串([])描述栈过程;为叫号选择结构;把单链表A→B在A后插入S;写各操作的边界。
需要一点提示
看后进先出、先进先出和指针修改顺序。
查看过程、答案与错因
括号:压(、压[、遇]弹[、遇)弹(,最终空栈;叫号用队列。插入先S.next=B再A.next=S。栈弹出须非空,队列须查空满,链表p有效且新节点分配成功;寻找p时间单算。