本文整理自譚浩強《C 程序設計》經典習題,涵蓋數字規律探索、數論基礎、組合數學、遞迴思維、陣列旋轉、循環鏈表模擬、字串比較、記憶體分配器八大核心主題。每題附帶完整邏輯解析與可執行代碼框架,適合夯實算法基本功。
第 k 項:`T_k = a * (10^{k-1} + 10^{k-2} + ... + 1) = a * (10^k - 1) / 9`
遞推關係:`T_1 = a`,`T_{k} = T_{k-1} * 10 + a`
long long calcSum(int a, int n) {
long long sum = 0, term = 0;
for (int i = 1; i <= n; i++) {
term = term * 10 + a; // 遞推生成項
sum += term;
}
return sum;
}
**注意**:`long long` 防溢位,`n` 較大時需高精度或取模。
`abc = a³ + b³ + c³`,其中 `a∈[1,9], b,c∈[0,9]`
for (int i = 100; i < 1000; i++) {
int a = i / 100, b = i / 10 % 10, c = i % 10;
if (a*a*a + b*b*b + c*c*c == i) printf("%d ", i);
}
// 輸出:153 370 371 407
**擴展**:n 位自戀數(Narcissistic Number),預計算 0-9 的 n 次方表加速。
真因子(不含自身)之和等於自身。如 `6 = 1+2+3`,`28 = 1+2+4+7+14`
int isPerfect(int n) {
if (n <= 1) return 0;
int sum = 1; // 1 是所有 n>1 的因子
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
sum += i;
if (i != n / i) sum += n / i; // 避免平方數重複
}
}
return sum == n;
}
1000 內完數:`6, 28, 496`。下一個 8128 > 1000。
void printPascal(int rows) {
int *cur = calloc(rows, sizeof(int));
for (int i = 0; i < rows; i++) {
cur[i] = 1; // 行尾為 1
for (int j = i - 1; j > 0; j--) // 逆序更新,避免覆蓋前值
cur[j] += cur[j-1];
for (int j = 0; j <= i; j++)
printf("%d ", cur[j]);
printf("\n");
}
free(cur);
}
`n = 483` → `print(48) + '3'` → `print(4) + '8' + '3'` → `'4' + '8' + '3'`
void intToStr(int n, char *buf, int *pos) {
if (n < 0) { buf[(*pos)++] = '-'; n = -n; }
if (n / 10) intToStr(n / 10, buf, pos);
buf[(*pos)++] = '0' + n % 10;
}
// 呼叫:int pos=0; intToStr(483, buf, &pos); buf[pos]='\0';
**關鍵**:先遞迴處理高位,再寫入低位,自然形成正序。
原陣列:`[A][B]`,其中 A 前 n-m 個,B 後 m 個。
目標:`[B][A]`
步驟:`reverse(A)` → `reverse(B)` → `reverse(整體)`
void reverse(int *a, int l, int r) {
while (l < r) { int t=a[l]; a[l]=a[r]; a[r]=t; l++; r--; }
}
void rotateRight(int *a, int n, int m) {
m %= n; if (m < 0) m += n;
reverse(a, 0, n-m-1);
reverse(a, n-m, n-1);
reverse(a, 0, n-1);
}
時間 O(n),空間 O(1),無需額外陣列。
`f(1) = 0`(0-based)
`f(i) = (f(i-1) + 3) % i`
int josephus(int n, int k) { // k=3
int survivor = 0; // 0-based
for (int i = 2; i <= n; i++)
survivor = (survivor + k) % i;
return survivor + 1; // 轉 1-based 編號
}
**模擬法**(教學用):循環鏈表或陣列標記,時間 O(nk),空間 O(n)。
int myStrcmp(const char *s1, const char *s2) {
while (*s1 && *s1 == *s2) { s1++; s2++; }
return (unsigned char)*s1 - (unsigned char)*s2; // 強制無號減法
}
**細節**:轉 `unsigned char` 避免負字元導致比較錯誤(如 ISO-8859-1 擴展字元)。
#define POOL_SIZE 10000
static char pool[POOL_SIZE];
static char *free_ptr = pool;
void *alloc(int n) {
if (free_ptr + n > pool + POOL_SIZE) return NULL; // 耗盡
void *p = free_ptr;
free_ptr += n;
return p;
}
**缺失**:無 `free`、無對齊、無碎片整理。真實分配器需維護空閒鏈表/紅黑樹。
| 題目 | 核心考點 | 進階方向 |
|---|---|---|
| 累加序列 | 遞推、溢位處理 | 矩陣快速冪求第 n 項 |
| 水仙花數 | 位拆解、窮舉優化 | n 位自戀數、預計算冪次表 |
| 完數 | 因子對稱性、√n 優化 | 歐拉證明偶完數形式、奇完數猜想 |
| 楊輝三角 | 組合數遞推、滾動陣列 | 盧卡斯定理求模質數組合數 |
| 遞迴轉字串 | 分治、棧幀理解 | 迭代版、任意進制轉換 |
| 陣列旋轉 | 三次反轉、原地算法 | 區間翻轉、區間循環移位 |
| 約瑟夫環 | 遞推公式、模擬 vs 數學 | k 變化、多輪倖存者集合 |
| strcmp | 字典序、無號字元 | strcoll 本地化比較、Unicode 正規化 |
| alloc | 指標算術、記憶體佈局 | Buddy System、Slab Allocator、malloc 實作 |
建議:先獨立實作每題,**再對比標準庫實現**(`qsort`、`bsearch`、`strlen`、`memmove` 等),體會工業級代碼的邊界處理與效能優化。