Master recursive thinking with GCD, Fibonacci, and linked list operations 通过 GCD、斐波那契和链表操作掌握递归思维
Recursion is when a function calls itself to solve a smaller version of the same problem. 递归是指一个函数调用自身来解决同一问题的更小版本。
⚡ Every recursive function needs TWO things: ⚡ 每个递归函数都需要两件事:
⚠️ Without a base case → infinite recursion → stack overflow crash! 没有基础情况 → 无限递归 → 栈溢出崩溃!
The Greatest Common Divisor (GCD) of two integers is the largest number that divides both with no remainder. 最大公因数(GCD)是能整除两个整数的最大数。
💡 Euclid's Key Identity: 💡 欧几里得核心公式:
gcd(a, b) = gcd(b, a % b)
Keep applying this until the remainder is 0. The other number is the GCD. 不断应用直到余数为 0,另一个数就是 GCD。
✅ This is one of the oldest known algorithms (~300 BC)! Clean, elegant, no helper function needed. 这是已知最古老的算法之一(约公元前 300 年)!简洁优雅,不需要辅助函数。
The rabbit problem produces the Fibonacci sequence. Each month's rabbit count = previous two months combined. 兔子问题产生斐波那契数列。每月兔子数 = 前两个月之和。
| Month月份 | Adult Pairs成年兔对 | Baby Pairs幼兔对 | Total Pairs总对数 | rabbits(n)函数返回值 |
|---|---|---|---|---|
| 0 | 0 | 1 | 1 | 2 (=1 pair × 2) |
| 1 | 1 | 0 | 1 | 2 |
| 2 | 1 | 1 | 2 | 4 |
| 3 | 2 | 1 | 3 | 6 |
| 4 | 3 | 2 | 5 | 10 |
| 5 | 5 | 3 | 8 | 16 |
⚡ Formula: rabbits(n) = rabbits(n-1) + rabbits(n-2) ⚡ 公式:rabbits(n) = rabbits(n-1) + rabbits(n-2)
Note: function returns number of individual rabbits (pairs × 2), so base cases return 2. 注意:函数返回兔子只数(对数 × 2),所以基础情况返回 2。
⚠️ Performance Problem!性能问题!
This naive recursion has O(2ⁿ) time complexity. rabbits(60) takes hours because the same values are recalculated millions of times.
这种朴素递归的时间复杂度是 O(2ⁿ)。rabbits(60) 要跑几小时,因为相同的值被重复计算了百万次。
Fix: Memoization (cache results) → O(n) time. 解决方案:记忆化(缓存结果)→ O(n) 时间。
A linked list is naturally recursive: it's either empty (NULL) or a node followed by a smaller linked list. 链表天然是递归的:它要么是空(NULL),要么是一个节点后跟着更小的链表。
📋 Linked List Recursion Template: 📋 链表递归模板:
Key insight: The last node has next == NULL.
核心洞察:最后一个节点的 next == NULL。
Key insight: max of list = max(current value, max of rest). 核心洞察:链表最大值 = max(当前值, 剩余链表最大值)。
This is the hardest task. Move the last node to the front without creating new nodes. 这是最难的任务。把最后一个节点移到最前面,不能创建新节点。
💡 Strategy: 3 cases 💡 策略:3 种情况
⚠️ Why does Case 2 need list->next = NULL?
为什么 Case 2 需要 list->next = NULL?
Without it: [2]→[1]→[2]→[1]→... infinite loop! Cutting the link first prevents the cycle. 不切断的话:[2]→[1]→[2]→[1]→... 无限循环!先切断链接才能防止循环。
Tasks 6–8 use a wrapper struct that contains the head pointer. Since we recurse on nodes (not the wrapper), we need a helper function. 任务 6–8 使用包含头指针的包装结构体。因为递归是对节点进行的,所以需要辅助函数。
Note: base case is node == NULL (not node->next == NULL) because the list can be empty (size 0).
注意:基础情况是 node == NULL 而非 node->next == NULL,因为链表可能是空的(size 为 0)。
Insert a value while keeping the list sorted. The helper returns the new head of the sublist. 在保持排序的同时插入一个值。辅助函数返回子链表的新头。
Insert at position n (0-indexed). If list is shorter than n, insert at the end. 在第 n 个位置插入(从 0 开始)。如果链表长度不足 n,则插入末尾。
| Task任务 | Base Case基础情况 | Recursive Case递归情况 | Helper?辅助函数? |
|---|---|---|---|
| gcd | b == 0 |
gcd(b, a%b) |
No否 |
| rabbits | months == 0 or 1 |
r(n-1) + r(n-2) |
No否 |
| listTail | next == NULL |
listTail(next) |
No否 |
| listMax | next == NULL |
max(val, listMax(next)) |
No否 |
| listShift | 1 or 2 nodes1 或 2 个节点 | recurse + relink递归 + 重连 | No否 |
| listSum | node == NULL |
val + sum(next) |
Yes是 |
| listInsertOrdered | NULL or val ≤ node |
recurse + relink递归 + 重连 | Yes是 |
| listInsertNth | n==0 or NULL |
recurse(n-1) + relink递归(n-1) + 重连 | Yes是 |