本地示范 / 先理解,再读代码
把步骤翻译成 C 与 SQL
正文先用伪代码说明过程。这里的完整C程序与固定数据SQL可下载,编译与执行核验记录见源码QA。无需在线运行服务或先安装开发环境即可阅读。
algorithms.c
/* C11. 本地教学程序:前提、过程与边界一起展示。
* a 指向至少 n 个有效 int;n==0 时允许 a==NULL。
* 线性扫描求和、半开区间二分、稳定插入排序、链表指针操作。
*/
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
/* 返回第一个不小于 key 的位置;输入必须非降序。 */
static size_t lower_bound(const int *a, size_t n, int key) {
size_t left = 0, right = n; /* 候选区间 [left,right) */
while (left < right) {
size_t mid = left + (right - left) / 2;
if (a[mid] < key) left = mid + 1;
else right = mid;
}
return left; /* 可等于 n,调用者不能直接访问 a[n] */
}
static void insertion_sort(int *a, size_t n) {
for (size_t i = 1; i < n; ++i) {
int value = a[i];
size_t j = i;
while (j > 0 && a[j - 1] > value) {
a[j] = a[j - 1];
--j;
}
a[j] = value;
}
}
struct Node { int value; struct Node *next; };
/* p 与 s 必须指向不同且存活的节点,s 尚未在链中。 */
static void insert_after(struct Node *p, struct Node *s) {
s->next = p->next; /* 先保住原来的后继 */
p->next = s;
}
/* 移除并返回后继,不释放由调用者拥有的节点。 */
static struct Node *remove_after(struct Node *p) {
struct Node *q = p->next;
if (q != NULL) { p->next = q->next; q->next = NULL; }
return q;
}
int main(void) {
int a[] = {3, 1, 2, 2};
insertion_sort(a, 4);
assert(a[0] == 1 && a[1] == 2 && a[2] == 2 && a[3] == 3);
assert(lower_bound(a, 4, 2) == 1);
assert(lower_bound(a, 4, 4) == 4);
assert(lower_bound(NULL, 0, 2) == 0);
insertion_sort(NULL, 0);
int single[] = {7}; insertion_sort(single, 1);
assert(lower_bound(single, 1, 6) == 0);
struct Node b = {7, NULL}, head = {4, &b}, s = {5, NULL};
insert_after(&head, &s);
assert(head.next == &s && s.next == &b);
assert(remove_after(&head) == &s && head.next == &b);
assert(remove_after(&b) == NULL);
int values[] = {2, 0, 5}; int sum = 0;
for (size_t i = 0; i < 3; ++i) sum += values[i];
assert(sum == 7);
printf("sorted: %d %d %d %d; sum: %d\n", a[0], a[1], a[2], a[3], sum);
return 0;
}library.sql
-- 固定教学数据;在空SQLite数据库中执行。
PRAGMA foreign_keys = ON;
CREATE TABLE Reader(id INTEGER PRIMARY KEY, name TEXT NOT NULL);
CREATE TABLE Loan(id INTEGER PRIMARY KEY,
reader_id INTEGER NOT NULL REFERENCES Reader(id),
returned INTEGER NOT NULL CHECK(returned IN (0,1)));
INSERT INTO Reader VALUES(1,'小林'),(2,'小周'),(3,'小陈');
INSERT INTO Loan VALUES(10,1,0),(11,1,0),(12,2,1),(13,2,0);
-- 结果 (1,2):先筛未还行,再分组筛选。
SELECT reader_id, COUNT(*) AS n FROM Loan WHERE returned=0
GROUP BY reader_id HAVING COUNT(*)>=2;
-- 结果 (1,2),(2,1),(3,0):条件放ON才能保留无借阅者。
SELECT r.id, COUNT(l.id) AS n FROM Reader r
LEFT JOIN Loan l ON l.reader_id=r.id AND l.returned=0
GROUP BY r.id ORDER BY r.id;
-- 结果 (1,2),(2,2),(3,0):全部借阅次数,COUNT不计补出的NULL。
SELECT r.id, COUNT(l.id) AS n FROM Reader r
LEFT JOIN Loan l ON l.reader_id=r.id GROUP BY r.id ORDER BY r.id;C语言提示:#include引入声明;int是整数;size_t是长度/下标类型;*表示指针;&取得地址;->访问指针所指结构体字段;NULL表示空指针;assert检查预期条件。函数输入边界在注释中声明,测试空输入、重复值、失败与正常路径。