Back to Final Review 返回 Final 复习
Final Exam — Coding 期末考试 — 编程题

Coding Review (Q1–Q5) 编程题复习(Q1–Q5)

60 marks · Based on multiple sample exams 60 分 · 基于多套模拟样卷

⚠️ Universal Constraints ⚠️ 通用约束(所有题)

  • No global or static variables → automatic zero marks禁止全局变量和静态变量 → 自动零分
  • No additional #includes禁止额外 #include
  • No hardcoded return values — must solve generally不能 hardcode 返回值,必须通用解法
  • You MAY define helper functions, structs, #defines可以定义辅助函数、结构体、宏
  • Memory leaks are NOT penalised (but memory errors may cause wrong results)内存泄漏不扣分(但内存错误可能导致结果不一致)

🔗 Q1 — Linked List: Recursive append 🔗 Q1 — 链表:递归 append

10 marks⭐ Easiest coding question

Append list l2 to the end of list l1. Return the combined list. 把链表 l2 接到 l1 后面,返回合并后的链表。

  • NO loops — while, for, do, goto all forbidden → zero marks禁止循环 — while/for/do/goto 全部禁止 → 零分
  • NO malloc/calloc — reuse existing nodes only禁止 malloc/calloc — 只能复用已有节点
  • MUST use recursion必须用递归

Core Insight 核心思路

Ask: what is append(l1, l2)? 问自己:append(l1, l2) 是什么?

  • If l1 is empty → just return l2如果 l1 是空的 → 直接返回 l2
  • If l1 is not empty → keep l1's head, but set its next = append(l1->next, l2)如果 l1 不是空的 → 保留 l1 的头节点,但它的 next = append(l1->next, l2)

Trace through example 示例追踪

append([3,1,4], [1,5]) = 3 → append([1,4], [1,5]) = 3 → 1 → append([4], [1,5]) = 3 → 1 → 4 → append([], [1,5]) = 3 → 1 → 4 → [1,5] ✓

Final Code 完整代码

struct node *append(struct node *l1, struct node *l2) { // base case: l1 is empty, just return l2 if (l1 == NULL) { return l2; } // recursive case: keep current head, fix its next l1->next = append(l1->next, l2); return l1; }

Edge Cases 边界情况

InputExpected Output
l1=[], l2=[1,3,3,7][1,3,3,7]
l1=[3,1,4], l2=[][3,1,4]
l1=[], l2=[][]

🔗 Q2 — Linked List: flas (First Longest Ascending Sublist) 🔗 Q2 — 链表:flas(第一最长严格递增子列)

10 marksMedium

Find the start node of the longest strictly-increasing consecutive sublist. If tied, return the first. Minimum length = 2. 找最长严格递增连续子列的起始节点。并列时返回第一个。最少2个元素才算。

  • NO arrays → zero marks禁止数组 → 零分
  • Do NOT modify the input list不能修改输入链表

The 4 Variables You Need 需要追踪的 4 个变量

  • curStartwhere current ascending run begins当前递增段的起始节点
  • curLenlength of current run当前段的长度
  • bestStartwhere the longest run so far begins目前最长段的起始节点
  • bestLenlength of the longest run so far目前最长段的长度

Walk through example 示例追踪

List: [1, 3, 2, 4, 5, 6] cur=1, next=3: 3>1 ✓ curLen=2 cur=3, next=2: 2<3 ✗ BREAK! curLen(2) > bestLen(0) → update best=1 reset: curStart=2, curLen=1 cur=2, next=4: 4>2 ✓ curLen=2 cur=4, next=5: 5>4 ✓ curLen=3 cur=5, next=6: 6>5 ✓ curLen=4 Loop ends. ⚠️ Must check last segment after loop! curLen(4) > bestLen(2) → update best=2 Return: pointer to node [2] ✓

⚠️ Most common mistake: forgetting to check the last segment after the loop. If list is [1,2,3,4,5], the loop never breaks — best never updates inside the loop! ⚠️ 最常见错误:循环结束后忘记检查最后一段。如果链表是 [1,2,3,4,5],循环里永远不会断,best 永远不会在循环内更新!

Final Code 完整代码

struct node *flas(struct node *l) { if (l == NULL || l->next == NULL) return NULL; struct node *bestStart = NULL; int bestLen = 0; struct node *curStart = l; int curLen = 1; for (struct node *cur = l; cur->next != NULL; cur = cur->next) { if (cur->next->val > cur->val) { curLen++; // still ascending } else { if (curLen > bestLen) { // broke: check if new best bestLen = curLen; bestStart = curStart; } curStart = cur->next; // reset current run curLen = 1; } } // ⚠️ Always check last segment after loop if (curLen > bestLen) { bestLen = curLen; bestStart = curStart; } if (bestLen < 2) return NULL; return bestStart; }

Edge Cases 边界情况

InputExpected
[]NULL
[5]NULL
[3,2,1]NULL (no ascending sublist)
[1,2,3,4,5]pointer to 1 (full list)
[5,1,2,3]pointer to 1 (length 3)
[1,2,3,2,3,4]pointer to first 1 (both length 3 — tie → first one)

🌐 Q3 — Graph: Max Degree Difference 🌐 Q3 — 图:最大度数差

10 marks⭐ Simple — Two nested loops

Directed graph. For each vertex compute |in-degree − out-degree|. Return the maximum over all vertices. 有向图。对每个节点计算 |入度 − 出度|,返回所有节点中的最大值。

💡 Reading the adjacency matrix: 💡 读邻接矩阵:

  • in-degree of v = scan column v (who points to v)v 的入度 = 扫描第 v 列(谁指向 v)
  • out-degree of v = scan row v (where v points to)v 的出度 = 扫描第 v 行(v 指向谁)

Final Code 完整代码

int maxDegreeDiff(Graph g) { int max = 0; for (int v = 0; v < g->nV; v++) { int inDeg = 0; int outDeg = 0; for (int u = 0; u < g->nV; u++) { if (g->edges[u][v]) inDeg++; // column v: who points to v if (g->edges[v][u]) outDeg++; // row v: where v points } int diff = inDeg - outDeg; if (diff < 0) diff = -diff; // abs value if (diff > max) max = diff; } return max; }

Note: Use manual absolute value if (diff < 0) diff = -diff to be safe. abs() requires <stdlib.h> which may already be included via Graph.h. 注意:为了安全,手写绝对值 if (diff < 0) diff = -diffabs() 需要 <stdlib.h>,Graph.h 里可能已经包含了。

🗺️ Q4 — Graph: dayTrip (BFS, dual edge types) 🗺️ Q4 — 图:dayTrip(BFS,双类型边)

15 marksMedium-Hard

Find all cities reachable from start vertex within 1 day. Two road types exist. 找从起点出发,在1天内能到达的所有城市。有两种道路类型。

  • STD road: 1 road = 1 full day1 条 = 1 整天
  • FAST road: 1 road = half a day → need 2 FAST to use a full day1 条 = 半天 → 2 条 FAST = 1 天

Reachability Rules 可到达规则

RouteCostReachable?
1 STD road1 day✅ Yes
1 FAST road0.5 day✅ Yes (under 1 day)
2 FAST roads1 day✅ Yes
1 FAST + 1 STD1.5 days❌ No
2 STD roads2 days❌ No

⚡ Key Insight: Use "half-day units" (0, 1, 2). Max allowed = 2. ⚡ 关键思路:用"半天"为单位(0, 1, 2),最大允许 = 2。

  • FAST edge: +1 half-day半天
  • STD edge: +2 half-days (= 1 full day)半天(= 1 整天)

BFS Trace (from vertex 0) BFS 追踪(从节点 0 出发)

dist[0]=0 → queue: [0] Pop 0 (dist=0): FAST neighbours 1,3 → dist[1]=1, dist[3]=1 ✓ add STD neighbours 2,3 → dist[2]=2 ✓ add (dist[3] already 1, skip) Pop 1 (dist=1): FAST neighbour 2 → dist[2] already set, skip STD neighbours → 1+2=3 > 2, skip Pop 3 (dist=1): FAST neighbour 5 → dist[5]=1+1=2 ≤ 2 ✓ add STD neighbours → 1+2=3 > 2, skip Pop 2, 5 (dist=2): all edges would exceed 2, skip Result: {1, 2, 3, 5} ✓

Final Code 完整代码

int dayTrip(Graph g, Vertex s, Vertex vs[]) { int dist[g->nV]; for (int i = 0; i < g->nV; i++) dist[i] = -1; dist[s] = 0; // BFS queue (array-based) int queue[g->nV]; int head = 0, tail = 0; queue[tail++] = s; while (head < tail) { int v = queue[head++]; for (int u = 0; u < g->nV; u++) { // FAST edge: +1 half-day if (g->fast[v][u] && dist[u] == -1) { dist[u] = dist[v] + 1; if (dist[u] <= 2) queue[tail++] = u; } // STD edge: +2 half-days (1 full day) if (g->std[v][u] && dist[u] == -1) { dist[u] = dist[v] + 2; if (dist[u] <= 2) queue[tail++] = u; } } } // Collect reachable vertices (exclude start) int count = 0; for (int i = 0; i < g->nV; i++) { if (dist[i] != -1 && i != s) { vs[count++] = i; } } return count; }

⚠️ Exam trap: Check the actual field names in Graph.h! They might be stdEdges/fastEdges or something else. Always look before writing. ⚠️ 考试陷阱:一定要看 Graph.h 里的实际字段名!可能是 stdEdges/fastEdges 或其他名字。先看再写。

Summary: what can reach where 总结:哪些节点可以继续走

Current dist =STD → new distFAST → new dist
0 (start)0+2=2 ✅0+1=1 ✅
1 (one FAST away)1+2=3 ❌1+1=2 ✅
2 (full day)2+2=4 ❌2+1=3 ❌

🌳 Q5 — BST: Min Diff at Level L 🌳 Q5 — BST:第 L 层最小差

15 marksHardest — O(n) required for full marks

Given a BST and integer l, find the minimum absolute difference between any two values on level l. Return 0 if fewer than 2 values on that level. 给 BST 和整数 l,找第 l 层所有节点值中的最小绝对差。如果该层少于 2 个节点,返回 0。

⚠️ O(n) solution = 15/15. Slower solution = max 9/15. ⚠️ O(n) 解法 = 15/15。更慢的解法 = 最多 9/15。

⚡ Key Insight: BFS level-by-level ⚡ 关键思路:BFS 逐层遍历

Use BFS with a queue that tracks (node, level). When we reach level l, collect values. BST left-to-right at any level is sorted → min diff = minimum of consecutive differences. 用 BFS,队列里存 (节点, 层级)。到达第 l 层时收集节点值。BST 在任意层从左到右的值是有序的 → 最小差 = 相邻值差的最小值。

Final Code 完整代码

int minDiff(struct node *t, int l) { if (t == NULL) return 0; // BFS queue: parallel arrays for node and level struct node *nodeQ[10000]; int levelQ[10000]; int head = 0, tail = 0; nodeQ[tail] = t; levelQ[tail] = 0; tail++; int prevVal = 0; int foundFirst = 0; int minD = 0; while (head < tail) { struct node *curr = nodeQ[head]; int currLevel = levelQ[head]; head++; if (currLevel == l) { if (!foundFirst) { prevVal = curr->val; foundFirst = 1; } else { int diff = curr->val - prevVal; // BST: left-to-right is sorted if (diff < 0) diff = -diff; if (minD == 0 || diff < minD) minD = diff; prevVal = curr->val; } } // Only enqueue children if we haven't reached level l yet if (currLevel < l) { if (curr->left != NULL) { nodeQ[tail] = curr->left; levelQ[tail] = currLevel + 1; tail++; } if (curr->right != NULL) { nodeQ[tail] = curr->right; levelQ[tail] = currLevel + 1; tail++; } } } return minD; }

✅ Why O(n)? ✅ 为什么是 O(n)?

Each node is visited at most once. We stop enqueuing children once we've reached level l — no wasted work going deeper. Total work = O(n). 每个节点最多访问一次。到达第 l 层后不再把子节点加入队列,不浪费时间继续往深走。总工作量 = O(n)。

Edge Cases 边界情况

SituationReturn
Level l doesn't exist in tree0
Only 1 node at level l0
2+ nodes at level lminimum |a - b| of any two adjacent values

📚 Bonus: listSetUnion 📚 附录:listSetUnion(链表集合并集)

Merge two sorted linked lists (sets) into one — each value appears only once. Both lists are sorted ascending. 合并两个有序链表(集合)为一个,每个值只出现一次。两个链表都是升序的。

Three cases per recursive step: 每次递归的三种情况:

  • l1 smaller: take l1 head, recurse with l1->next and l2l1 更小:取 l1 头,用 l1->next 和 l2 递归
  • l2 smaller: take l2 head, recurse with l1 and l2->nextl2 更小:取 l2 头,用 l1 和 l2->next 递归
  • Equal: take one, skip the other → recurse with l1->next and l2->next相等:取一个,跳过另一个 → 用 l1->next 和 l2->next 递归
struct node *listSetUnion(struct node *l1, struct node *l2) { if (l1 == NULL) return l2; if (l2 == NULL) return l1; if (l1->val == l2->val) { // equal: take l1, skip l2, advance both l1->next = listSetUnion(l1->next, l2->next); return l1; } if (l1->val < l2->val) { l1->next = listSetUnion(l1->next, l2); return l1; } l2->next = listSetUnion(l1, l2->next); return l2; }

💡 Why it doesn't get stuck: Every recursive call advances at least one pointer. Both eventually reach NULL, terminating the recursion. 💡 为什么不会卡住:每次递归至少让一个指针往前走。两个最终都会到 NULL,递归必然终止。

🎯 Test Your Coding Knowledge 🎯 测试你的编程知识

20 questions on linked lists, graphs, BFS, and BST — pure concept testing. 20 道题覆盖链表、图、BFS、BST — 纯知识点测试。

Start Coding Quiz → 开始编程测验 →