核心思想
这本书的核心思想是:选择合适的数据结构,并设计高效的算法来解决问题,它不仅仅是罗列知识点,而是强调数据结构与算法之间的内在联系,并分析算法的时间复杂度和空间复杂度,以评估其效率。

(图片来源网络,侵删)
第一部分:基础
是后续学习的基础。
C 语言基础回顾
虽然这本书是 C++ 的,但用 C 语言实现时,需要特别注意以下几点:
- 指针:C 语言的灵魂,所有动态数据结构(链表、树、图)的实现都离不开指针,必须熟练掌握指针的声明、解引用、指针运算以及指针作为函数参数和返回值。
- 结构体:用于创建自定义数据类型,是构建复杂数据结构(如链表节点、树节点、图顶点)的基石。
- 动态内存分配:使用
malloc(),calloc(),realloc(), 和free()来在程序运行时动态地创建和管理内存,这是实现动态数据结构的关键。 - 函数指针:在某些高级算法(如回调函数、排序算法的比较函数)中非常有用。
示例:C 语言中的结构体和指针
#include <stdio.h>
#include <stdlib.h>
// 定义一个结构体,用于表示链表的节点
struct Node {
int data; // 数据域
struct Node* next; // 指针域,指向下一个节点
};
// 函数:创建一个新节点
struct Node* createNode(int value) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
if (newNode == NULL) {
printf("内存分配失败!\n");
exit(1);
}
newNode->data = value;
newNode->next = NULL;
return newNode;
}
// 函数:打印链表
void printList(struct Node* head) {
struct Node* current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
int main() {
// 创建链表: 1 -> 2 -> 3
struct Node* head = createNode(1);
head->next = createNode(2);
head->next->next = createNode(3);
printList(head); // 输出: 1 -> 2 -> 3 -> NULL
// 释放内存 (在实际应用中需要遍历释放所有节点)
free(head->next->next);
free(head->next);
free(head);
return 0;
}
算法分析
这是衡量算法好坏的科学。

(图片来源网络,侵删)
- 时间复杂度:估算算法执行时间与输入规模
n之间的关系,使用大 O 表示法,如 O(1), O(log n), O(n), O(n log n), O(n²)。 - 空间复杂度:估算算法所需额外空间与输入规模
n之间的关系。 - 最好、最坏、平均情况分析:全面评估算法在不同输入下的性能。
第二部分:基本数据结构
这是数据结构的核心内容。
数组
- 特点:在内存中连续存储,支持随机访问(通过索引),查找快(O(1)),但插入和删除慢(O(n)),因为需要移动大量元素。
- C 语言实现:原生数组或动态分配的数组。
- 应用:需要频繁随机访问的场景,如查找表。
链表
- 特点:元素在内存中不连续存储,每个节点包含数据和指向下一个节点的指针,插入和删除快(O(1),已知节点位置),但查找慢(O(n)),因为必须从头遍历。
- C 语言实现:使用
struct和 指针实现。- 单链表
- 双链表:每个节点有
next和prev指针。 - 循环链表:尾节点的
next指向头节点。
- 应用:需要频繁插入和删除的场景,如实现队列、内存管理中的空闲链表。
栈
- 特点:后进先出 的数据结构。
- C 语言实现:
- 基于数组:用一个数组和一个栈顶指针实现。
- 基于链表:用链表的头插法/头删法实现(效率更高)。
- 应用:函数调用栈、表达式求值(括号匹配、后缀表达式)、浏览器的前进/后退。
队列
- 特点:先进先出 的数据结构。
- C 语言实现:
- 基于数组:使用循环数组来避免“假溢出”。
- 基于链表:效率最高,一个指针指向头(出队),一个指针指向尾(入队)。
- 应用:任务调度、广度优先搜索、消息缓冲区。
第三部分:高级数据结构
这些是为解决特定复杂问题而设计的数据结构。
树
- 二叉树:每个节点最多有两个子节点。
- 二叉搜索树:左子树所有值 < 根节点值 < 右子树所有值,支持高效的查找、插入、删除(平均 O(log n),最坏 O(n))。
- 堆:一种特殊的完全二叉树。
- 最大堆:父节点的值 >= 子节点的值。
- 最小堆:父节点的值 <= 子节点的值。
- C 语言实现:使用
struct定义节点,包含数据和指向左右子节点的指针。 - 应用:
- BST:实现字典、有序映射。
- 堆:优先级队列、堆排序。
平衡查找树
为了解决 BST 在最坏情况下退化为链表(O(n))的问题而生。
- AVL 树:任何节点的两个子树的高度差最多为 1,通过旋转操作维持平衡。
- 红黑树:一种弱平衡树,通过着色和旋转操作维持平衡,实现起来比 AVL 树简单,在插入/删除时旋转次数更少。
- C 语言实现:非常复杂,节点需要增加额外的信息(如 AVL 树的平衡因子,红黑树的颜色)。
- 应用:C++ STL 中的
map和set、Linux 内核的完全公平调度器、数据库索引。
字典 / 哈希表
- 特点:通过哈希函数将键映射到数组的一个索引上,实现近乎 O(1) 的平均查找、插入和删除。
- C 语言实现:
- 哈希函数:如除法散列、乘法散列。
- 冲突解决:
- 链地址法:每个哈希桶是一个链表,冲突的元素都挂到链表上。
- 开放地址法:当冲突发生时,按一定规则(线性探测、二次探测)寻找下一个空位。
- 应用:数据库索引、缓存、符号表。
优先队列
- 特点:一种特殊的队列,每次取出的是具有最高(或最低)优先级的元素。
- C 语言实现:堆 是实现优先队列的最佳数据结构。
- 应用:任务调度、Dijkstra 算法、A* 算法。
并查集
- 特点:一种处理“集合”合并与查询问题的数据结构。
- C 语言实现:通常使用树结构实现,并采用路径压缩和按秩合并两种优化策略。
- 应用: Kruskal 最小生成树算法、求连通分量。
图
- 特点:由顶点和边组成,用于表示多对多的关系。
- C 语言实现:
- 邻接矩阵:用一个二维数组表示,
matrix[i][j] = 1表示顶点i和j之间有边,适合稠密图。 - 邻接表:为每个顶点维护一个链表(或动态数组),存储其所有邻接点,适合稀疏图。
- 邻接矩阵:用一个二维数组表示,
- 应用:社交网络、地图导航、网络拓扑。
第四部分:算法
数据结构的操作和问题的解决最终都由算法实现。

(图片来源网络,侵删)
排序算法
- 简单排序:冒泡排序、选择排序、插入排序。(O(n²))
- 高级排序:
- 快速排序:分治思想,平均 O(n log n)。
- 归并排序:分治思想,稳定排序,O(n log n)。
- 堆排序:利用堆数据结构,O(n log n)。
- C 语言实现:是练习指针和数组的绝佳范例。
查找算法
- 顺序查找:O(n)。
- 二分查找:要求数组有序,O(log n)。
- 二叉搜索树查找:平均 O(log n),最坏 O(n)。
- 哈希表查找:平均 O(1)。
图算法
- 广度优先搜索:使用队列实现,找无权图的最短路径。
- 深度优先搜索:使用栈或递归实现,检测环、拓扑排序。
- 最小生成树:Prim 算法、Kruskal 算法。
- 最短路径:Dijkstra 算法(非负权)、Floyd-Warshall 算法(任意权)。
动态规划
- 思想:将复杂问题分解为子问题,并存储子问题的解以避免重复计算。
- 应用场景:背包问题、最长公共子序列、斐波那契数列。
- C 语言实现:通常使用数组来存储子问题的解(状态转移方程)。
学习建议
- 理论与实践结合:不要只看书上的伪代码,对于每一个数据结构和算法,都用 C 语言亲手实现一遍,这个过程会让你对指针、内存和算法细节有更深刻的理解。
- 画图辅助理解:对于链表、树、图等结构,画图是理解其操作(如插入、删除、遍历)的最有效方式。
- 分析复杂度:每次实现完一个算法,都要主动分析其时间和空间复杂度,并思考其优缺点。
- 刷题巩固:在 LeetCode、HackerRank 等平台上找相关的题目来练习,实现链表、二叉树、栈、队列,然后做相关的算法题(如反转链表、二叉树遍历、有效的括号等)。
- 阅读优秀代码:阅读一些开源项目(如 Redis, Linux Kernel)中是如何实现这些基础数据结构的,可以学到很多工程实践中的技巧。
希望这份详细的梳理能帮助您更好地学习《数据结构、算法与应用:C 语言描述》这门课程,祝您学习愉快!
