- 核心思想:为什么数据结构与算法是编程的灵魂?
- C 语言实现基础:用 C 语言实现 DS&A 所需的关键特性。
- 核心数据结构详解:逐一讲解常见数据结构及其 C 语言实现。
- 核心算法分类:介绍不同类型的算法及其 C 语言实现示例。
- 学习资源与建议:推荐书籍和在线资源。
核心思想:为什么是数据结构与算法?
- 数据结构是数据的组织、管理和存储格式,它旨在高效地访问和修改数据,你可以把它想象成图书馆的图书分类法(按作者、按主题、编号等),不同的分类法(数据结构)决定了你找书的速度(算法效率)。
- 算法是解决特定问题的一系列清晰、有限的指令,它是在数据结构上进行操作的方法,你可以把它想象成“找书的步骤”。
C 语言的角色:C 语言提供了指针、结构体、内存管理等底层工具,让我们可以精确地控制数据在内存中的布局和操作,从而深刻理解数据结构的本质和算法的性能。

(图片来源网络,侵删)
C 语言实现基础
在 C 语言中实现数据结构与算法,主要依赖以下几个特性:
a. 指针
指针是 C 语言的灵魂,它存储了另一个变量的内存地址。
- 用途:
- 动态内存分配:在运行时根据需要分配内存,这是创建动态数据结构(如链表、树、图)的基础。
- 高效传参:通过传递指针而不是整个大结构体,可以避免大量的数据拷贝,提高效率。
- 构建复杂结构:通过指针将不同的结构体变量连接起来,形成链、树、图等复杂关系。
int a = 10;
int *p = &a; // p 指向 a 的地址
printf("%d", *p); // 通过指针访问 a 的值,输出 10
b. 结构体
结构体允许我们将不同类型的数据组合成一个单一的、自定义的类型。
- 用途:定义数据结构的“节点”或“元素”,链表的节点、图的顶点等。
// 定义一个链表节点的结构体
typedef struct Node {
int data; // 数据域
struct Node *next; // 指针域,指向下一个节点
} Node;
typedef 关键字为 struct Node 创建了一个别名 Node,使得代码更简洁。

(图片来源网络,侵删)
c. 内存管理函数
malloc(size_t size):分配一块指定大小的内存,返回一个void*指针,使用时需要强制类型转换。calloc(size_t num, size_t size):分配 num 个 size 大小的连续内存,并初始化为 0。free(void *ptr):释放之前通过malloc或calloc分配的内存。防止内存泄漏!realloc(void *ptr, size_t new_size):调整之前分配的内存块大小。
核心数据结构详解
a. 数组
数组是一块连续的内存空间,存储相同类型的元素。
- 特点:
- 优点:随机访问快(O(1)),通过索引可以直接找到元素。
- 缺点:大小固定(静态数组),插入和删除慢(O(n)),因为需要移动大量元素。
- C 语言实现:C 语言原生支持数组。
int arr[10]; // 静态数组 int *dynamic_arr = (int*)malloc(10 * sizeof(int)); // 动态数组 // 使用... free(dynamic_arr);
b. 链表
链表由一系列节点组成,每个节点包含数据和指向下一个节点的指针。
- 特点:
- 优点:大小动态,插入和删除快(O(1),已知位置前/后)。
- 缺点:不支持随机访问,访问元素慢(O(n)),因为需要从头遍历。
- C 语言实现:必须使用结构体和指针手动实现。
// 节点定义 (同上)
typedef struct Node {
int data;
struct Node *next;
} Node;
// 在链表头部插入节点
Node* insertAtHead(Node *head, int value) {
Node *newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = head;
return newNode; // 新节点成为新的头
}
// 遍历打印链表
void printList(Node *head) {
Node *current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
c. 栈
栈是一种 后进先出 的数据结构。
- 特点:只允许在一端(栈顶)进行插入(入栈
push)和删除(出栈pop)操作。 - C 语言实现:通常用数组或链表实现,这里用数组实现。
#define MAX_SIZE 100
typedef struct {
int items[MAX_SIZE];
int top;
} Stack;
void initStack(Stack *s) {
s->top = -1;
}
int isEmpty(Stack *s) {
return s->top == -1;
}
void push(Stack *s, int value) {
if (s->top == MAX_SIZE - 1) {
printf("Stack Overflow\n");
return;
}
s->items[++s->top] = value;
}
int pop(Stack *s) {
if (isEmpty(s)) {
printf("Stack Underflow\n");
return -1;
}
return s->items[s->top--];
}
d. 队列
队列是一种 先进先出 的数据结构。

(图片来源网络,侵删)
- 特点:在一端(队尾)插入,另一端(队首)删除。
- C 语言实现:通常用链表实现(避免数组移动元素的麻烦),这里用链表实现。
// 队列节点 (与链表节点相同)
typedef struct QNode {
int data;
struct QNode *next;
} QNode;
typedef struct {
QNode *front; // 队首指针
QNode *rear; // 队尾指针
} Queue;
void initQueue(Queue *q) {
q->front = q->rear = NULL;
}
void enqueue(Queue *q, int value) {
QNode *newNode = (QNode*)malloc(sizeof(QNode));
newNode->data = value;
newNode->next = NULL;
if (q->rear == NULL) { // 队列为空
q->front = q->rear = newNode;
} else {
q->rear->next = newNode;
q->rear = newNode;
}
}
int dequeue(Queue *q) {
if (q->front == NULL) {
printf("Queue is Empty\n");
return -1;
}
QNode *temp = q->front;
int value = temp->data;
q->front = q->front->next;
if (q->front == NULL) {
q->rear = NULL;
}
free(temp);
return value;
}
e. 树
树是一种分层数据结构,由节点和边组成,最常见的是 二叉树 和 二叉搜索树。
- 二叉搜索树:左子树所有节点值 < 根节点值 < 右子树所有节点值,查找、插入、删除的平均时间复杂度为 O(log n)。
- C 语言实现:递归是操作树的天然方式。
typedef struct TreeNode {
int data;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
// 插入节点到 BST
TreeNode* insert(TreeNode *node, int value) {
if (node == NULL) {
TreeNode *newNode = (TreeNode*)malloc(sizeof(TreeNode));
newNode->data = value;
newNode->left = newNode->right = NULL;
return newNode;
}
if (value < node->data) {
node->left = insert(node->left, value);
} else if (value > node->data) {
node->right = insert(node->right, value);
}
return node; // 值已存在,不做操作
}
// 中序遍历 (输出有序序列)
void inorderTraversal(TreeNode *root) {
if (root != NULL) {
inorderTraversal(root->left);
printf("%d ", root->data);
inorderTraversal(root->right);
}
}
f. 哈希表
哈希表通过哈希函数将键映射到数组中的一个位置,以实现平均 O(1) 的查找、插入和删除。
- C 语言实现:C 语言标准库没有内置哈希表,需要自己实现,最常见的是 链地址法 解决冲突,即每个哈希桶是一个链表。
#define TABLE_SIZE 10
typedef struct HashNode {
int key;
int value;
struct HashNode *next;
} HashNode;
typedef struct {
HashNode *buckets[TABLE_SIZE];
} HashTable;
unsigned int hash(int key) {
return key % TABLE_SIZE;
}
void insert(HashTable *ht, int key, int value) {
unsigned int index = hash(key);
HashNode *newNode = (HashNode*)malloc(sizeof(HashNode));
newNode->key = key;
newNode->value = value;
newNode->next = ht->buckets[index];
ht->buckets[index] = newNode;
}
int search(HashTable *ht, int key) {
unsigned int index = hash(key);
HashNode *node = ht->buckets[index];
while (node != NULL) {
if (node->key == key) {
return node->value;
}
node = node->next;
}
return -1; // 未找到
}
核心算法分类
算法是操作数据结构的方法。
a. 排序算法
对数据集合进行排列。
-
冒泡排序:简单但低效 (O(n²))。
void bubbleSort(int arr[], int n) { for (int i = 0; i < n-1; i++) { for (int j = 0; j < n-i-1; j++) { if (arr[j] > arr[j+1]) { // 交换 int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } -
快速排序:分治法,平均高效 (O(n log n))。
int partition(int arr[], int low, int high) { int pivot = arr[high]; int i = (low - 1); for (int j = low; j <= high - 1; j++) { if (arr[j] < pivot) { i++; // 交换 arr[i] 和 arr[j] int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } // 交换 arr[i+1] 和 arr[high] int temp = arr[i + 1]; arr[i + 1] = arr[high]; arr[high] = temp; return (i + 1); } void quickSort(int arr[], int low, int high) { if (low < high) { int pi = partition(arr, low, high); quickSort(arr, low, pi - 1); quickSort(arr, pi + 1, high); } }
b. 搜索算法
在数据集合中查找特定元素。
-
线性搜索:简单,适用于无序数据 (O(n))。
int linearSearch(int arr[], int n, int x) { for (int i = 0; i < n; i++) { if (arr[i] == x) { return i; } } return -1; } -
二分搜索:高效,但要求数据有序 (O(log n))。
int binarySearch(int arr[], int l, int r, int x) { while (l <= r) { int m = l + (r - l) / 2; if (arr[m] == x) { return m; } if (arr[m] < x) { l = m + 1; } else { r = m - 1; } } return -1; }
c. 图算法
图由顶点和边组成。
-
深度优先搜索:使用栈(或递归)尽可能深地探索分支。
// 假设图用邻接表表示,visited是标记数组 void DFS(int v, int *visited, struct Graph *graph) { visited[v] = 1; printf("%d ", v); struct Node* pCrawl = graph->adjLists[v]; while (pCrawl) { if (!visited[pCrawl->dest]) { DFS(pCrawl->dest, visited, graph); } pCrawl = pCrawl->next; } } -
广度优先搜索:使用队列逐层探索。
void BFS(int startVertex, int *visited, struct Graph *graph) { Queue q; initQueue(&q); visited[startVertex] = 1; enqueue(&q, startVertex); while (!isEmpty(&q)) { int currentVertex = dequeue(&q); printf("%d ", currentVertex); struct Node* pCrawl = graph->adjLists[currentVertex]; while (pCrawl) { if (!visited[pCrawl->dest]) { visited[pCrawl->dest] = 1; enqueue(&q, pCrawl->dest); } pCrawl = pCrawl->next; } } }
学习资源与建议
书籍
- 《数据结构与算法分析:C 语言描述》:Mark Allen 著,经典中的经典,理论与实践结合得非常好,是很多人的入门首选。
- 《C 和指针》:Kenneth Reek 著,如果你想先彻底搞懂 C 语言的指针,再看 DS&A,这本书是绝佳选择。
- 《算法导论》:Thomas H. Cormen 等著,算法领域的“圣经”,内容全面且深入,但比较硬核,适合有一定基础后深入学习。
在线资源
- GeeksforGeeks:https://www.geeksforgeeks.org/ 内容极其丰富,几乎所有数据结构和算法都有 C/C++ 实现,并有详细解释。
- GitHub - "The-Algorithms":https://github.com/TheAlgorithms/C 一个庞大的开源项目,用 C 语言实现了海量的算法,代码质量高,是很好的参考。
- Coursera / edX:搜索 "Data Structures" 或 "Algorithms",有许多世界名校(如普林斯顿、斯坦福)的免费或付费课程,通常有 C++ 实现,但思想是相通的。
学习建议
- 先理解,再编码:在写代码前,先用纸笔画出数据结构的结构图,手动模拟算法的执行步骤。
- 亲手实现:不要只看不练,把每个数据结构和核心算法都自己用 C 语言实现一遍,这是加深理解的唯一途径。
- 分析复杂度:学习使用大 O 表示法来分析你实现的算法的时间复杂度和空间复杂度。
- 调试与测试:使用
printf或调试器来跟踪你的代码,确保它在各种边界条件下(如空列表、单元素列表)都能正确工作。 - 从简单开始:先掌握数组和指针,再实现链表、栈、队列,然后是树和图,最后是哈希表等更复杂的结构。
掌握用 C 语言描述数据结构与算法,会让你对计算机程序的运行原理有更深刻的认识,极大地提升你的编程内功和解决实际问题的能力,祝你学习顺利!
