給定 N 個整數 (N ≤ 1011,即 1000 億),每個數範圍 [0, 231-1],要求**有序輸出最大的前 10 個**。數據量極大,無法全部載入內存,需流式處理、O(1) 額外空間、單次掃描完成。
{ int data; Node *next; },預分配 10 個節點組成初始鏈表,值初始化為 -1(哨兵)。x:
node->data <= x 的位置(即 x 應插入的前驅節點)。x 比現有最大值還大),則 x 成為新頭,原頭節點踢出鏈表(保持長度 10)。x 插入,尾部多出的節點自動丟棄。x 小於等於當前第 10 大值,直接丟棄。read() 系統調用),減少系統調用開銷。
#define BUFF_LEN 4096
typedef struct queue_s {
int data;
struct queue_s *next;
} queue_t;
// 預分配 10 個節點,形成初始降序鏈表:-1 → -1 → ... → -1 → NULL
queue_t *rbuff = malloc(sizeof(queue_t) * 10);
bzero(rbuff, sizeof(queue_t) * 10);
for (int i = 0; i < 9; i++) {
(rbuff + i)->next = (rbuff + i + 1);
(rbuff + i)->data = -1;
}
(rbuff + 9)->data = -1;
(rbuff + 9)->next = NULL;
// 處理緩衝區中每個整數 x = *(sbuff + i)
queue_t **tmp = &rbuff; // 指向「指向當前節點的指針」(雙重指針,便於修改前驅的 next)
while (*tmp != NULL && x >= (*tmp)->data) {
tmp = &((*tmp)->next); // 移動到下一個節點的 next 字段地址
}
// 若 tmp 仍指向頭節點,說明 x 比所有現有值都大,需插入頭部
if (*tmp == rbuff) {
// 將 x 存入頭節點,原頭節點變為第二節點,尾部自動截斷
rbuff->data = x;
if (rbuff->next != *tmp) { // 這裡邏輯較繞,實際是調整鏈表順序
// ... 節點搬移代碼 ...
}
continue; // 本質上完成了「新值入頭,舊值後移,尾部丟棄」
}
// 正常中間/尾部插入:tmp 指向的是前驅節點的 next 字段
// *tmp 是插入位置的原節點(或 NULL)
// 將原頭節點數據覆寫為 x,並將原頭節點移動到插入位置
rbuff->data = x;
if (*tmp != rbuff->next) {
queue_t *t = rbuff;
rbuff = rbuff->next; // 頭指針後移
t->next = *tmp; // 原頭節點插入到 tmp 位置
*tmp = t; // 前驅節點的 next 指向原頭節點
}
read(STDIN_FILENO, buff, 4MB),每次讀 4MB,減少系統調用。int 數組,避免文本解析 (scanf) 的巨大開銷。INFO 宏控制輸出比較次數、寫內存次數、耗時,便於性能分析。| 指標 | 數值 |
|---|---|
| 總輸入數 | 1,000,000 |
| 總比較次數 | 2,001,654 (≈ 2N) |
| 總寫內存次數 | 552 (極少,得益於固定鏈表原地修改) |
| 總耗時 | 0.014687 秒 |
針對「超大 N、極小 K」的 Top-K 場景,**固定大小有序鏈表/數組 + 流式單遍掃描 + 大塊二進制 I/O** 是工程上最簡潔、高效、內存常數級的解法。本文代碼展示了 C 語言在系統級性能優化中的極致控制力:指針操作、雙重指針鏈表重組、零拷貝緩衝區、系統調用級 I/O,將 100 萬數處理壓縮至 14ms 級別,為 1000 億級數據提供了可線性擴展的基石方案。