你没看错,这么快就要学习算法了,虽然我们的 C 语言基础还不够扎实,但是我们可以先从 一些基本概念和算法入门的前置知识 开始。
在实际讨论时,我们通常会将 “数据结构与算法” 简称为 “算法”。
第一次听说算法?#
在高中阶段,我们就已经学习过算法框图,但是那个时候我们还没有接触到计算机,所以并没有真正理解算法的概念。
算法的定义#
算法(Algorithm)是指用来解决特定问题的一系列指令,它是指令的有序集合,是指一系列操作,一步一步地解决一个问题。
在计算机程序中,算法常常与数据结构联系在一起,算法是指用来解决特定问题的指令集合,而数据结构则是指用来存储和组织数据的集合。

简单理解就是:算法是对数据进行 增删改查 的方法。数据结构是程序的骨架,而算法则是程序的灵魂。
数据结构#
还没开始学呢,就冒出来了一个看起来很高级的新词:数据结构。
数据结构 是计算机存储、组织数据的方式,它是指数据 元素 的集合、关系和规则,以及这些数据元素之间的相互关系。
举个简单的例子:
整型 int 型数据,它可以存储整数,一堆整型数据组成的集合叫作 数组,数组就是一种数据结构。
int scores[10] = {85, 90, 78, 88, 95, 82, 79, 91, 76, 84};
显而易见数组有更多的优势:
- 数据量大时,可以快速访问任意元素;
- 数组可以动态调整大小,方便添加或删除元素;
- ……
办法总比困难多#
解决一个问题,肯定不止一种办法,写程序也是一样。
算法优劣对比#
不同的算法在不同的场景下,有着不同的优势。那么如何衡量一个算法的优劣呢?
—— 用时少、花费小
举个例子:
从上海到北京,有多种交通工具可以选择。假设你要选择最快捷的交通工具,你会怎么做?
- 第一种方法:坐飞机。
- 第二种方法:坐高铁。
- 第三种方法:坐长途汽车。
结合用时少,花费小的原则对以上方法进行对比:
| 方法 | 用时 | 花费 | 优势 | 劣势 |
|---|---|---|---|---|
| 坐飞机 | 2-3小时 | 700-2000元 | 用时最少 | 费用最高 |
| 坐高铁 | 4.5-6小时 | 550-1200元 | 用时较短,费用适中 | 相对飞机稍慢 |
| 坐长途汽车 | 12-15小时 | 300-500元 | 费用最低 | 用时最长 |
- 坐飞机:最快,但价格较高且流程较为复杂。
- 坐高铁或火车:时间适中,价格也较合理,适合大多数人。
- 坐长途汽车:费用最少,但需要忍受较长的旅途时间。
没有最好的算法,只有最合适的算法。
对于一个 有钱并且赶时间 的人来说,飞机可能最适合他。
对于一个喜欢沿途风景的 旅行者 来说,高铁或火车可能更合适。
对于一个 贫穷 的人来说,坐长途汽车可能是最经济的选择。
在程序中也是一样,每个人的电脑配置不同,需求也不同,所以算法的选择也可能有所不同。
算法与程序#
虽然算法多种多样,但程序都有着共同的目标:解决问题。算法的目标是解决问题,所以算法的设计者往往会考虑众多因素:
在计算机程序中一个算法应该考虑的往往是:
正确性:正确性是指算法能够满足具体问题的需求,程序运行正常,无语法错误,能够通过典型的软件测试,达到预期的需求。可读性:可读性指的是算法遵循标识符命名规则,简洁易懂,注释语句恰当,方便自己和他人阅读,便于后期修改和调试。健壮性:健壮性指的是算法对非法数据以及操作有较好的反应和处理。
当然除了以上三个目标,好的算法还会考虑到其他因素:
就是上边提到的 用时少、花费小,算法的设计者也会考虑到算法的效率。
在计算机中所代表的就是所需运行时间更少(时间复杂度更低)、占用内存空间更小(空间复杂度更低)。
生活中的算法#
其实在接触计算机和编程之前,我们就已经在生活中使用算法了。比如:
查字典(二分查找法)#

从 数据结构 的角度,我们可以把字典视为一个已排序的 “数组”;从 算法 的角度,我们可以将上述查字典的一系列操作看作 “二分查找”。
斗地主(插入排序法)#

上述整理扑克牌的方法本质上是 “插入排序” 算法,它在处理小型数据集时非常高效。许多编程语言的排序库函数中都有插入排序的身影。
比如:C++ 标准库中的 std::list::sort()
#include <iostream>
#include <list> // For std::list
int main() {
// Create a list of integers
std::list<int> myList = {12, 11, 13, 5, 6};
// Sort the list using the list's sort() method
myList.sort();
std::cout << "Sorted list: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
那数据结构呢 ?#
在生活中也可以看到数据结构的影子。比如:
你有一个书架,书架上放着很多书籍,你按照某种顺序(例如按字母顺序或按大小)排列这些书籍。
书架与数据结构的对应关系:
- 书架上的每本书 就像数据结构中的 元素。
- 书架的排列顺序 类似于数据结构中元素的 顺序,比如数组或链表。
- 添加新书 相当于在数据结构中插入新元素。
- 取一本书 就像在数据结构中访问特定的元素。
- 移走一本书 就是删除数据结构中的一个元素。
- ……
在书架的例子中:
- 如果你按照书籍的大小从小到大排列,那就像是一个 排序数组。
- 如果你将书架设计成可以灵活插入和移除书籍,那么它就像一个 链表,每本书可以随意插入和移除,位置也可以动态调整。
设计算法#
设计算法简单来说就是设计一个工作流。
自己设计排序#
假设你有一副扑克牌,点面全是数字,你想把这副牌按照数字大小排序,你会怎么做?
对于一个小数组:[3, 1, 4, 1],我们要用刚才提到的插入排序法进行排序。
开始排序:从第二个元素 1 开始排序。
保存当前元素:将 1 保存到 key 变量中。
寻找插入位置:
向前检查第一个元素 3,因为 3 > 1,所以需要把 3 移到右边。
移动元素:
将 3 移到右边,变成 [3, 3, 4, 1]。
插入当前元素:
把 key(1)放到前面,变成 [1, 3, 4, 1]。
继续处理下一个元素:
对下一个 4 执行相同操作,最后数组变成 [1, 3, 4, 1],继续处理最后一个 1。
最终,整个数组变成 [1, 1, 3, 4],这是经过插入排序后的结果。
用 C 语言来实现上述步骤:
#include <stdio.h>
// 插入排序函数
void insertionSort(int arr[], int n) {
int i, key, j;
for (i = 1; i < n; i++) {
key = arr[i];
j = i - 1;
// 移动元素,将大于 key 的元素移到右边
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
// 打印数组
void printArray(int arr[], int size) {
int i;
for (i = 0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}
int main() {
int arr[] = {3, 1, 4, 1};
int n = sizeof(arr) / sizeof(arr[0]);
printf("Original array: \n");
printArray(arr, n);
insertionSort(arr, n);
printf("Sorted array: \n");
printArray(arr, n);
return 0;
}
写完之后,相见恨晚。#
有的编程语言已经提供了现成的排序算法,比如 Python。
arr = [3, 1, 4, 1]
arr.sort()
print(arr)
为什么要学习算法?#
如上文提到的 Python 的 sort() 方法、 C++ 标准库中的 std::list::sort() 就是例子。既然有了现成的算法,为什么还要自己设计一个呢?
自己会在学习和设计过程中感觉到快乐愉悦。自己设计的算法可以更好地满足需求。自己设计的算法可以深入理解算法的原理。- 为了比赛拿奖,为了刷题,为了面试,为了面试官的面试技巧。
作为一名程序员,通常情况下会优先使用封装好的库函数来解决问题,尤其是在库函数能够高效、可靠地满足需求时。这是因为库函数通常经过优化和广泛测试,可以提高开发效率并减少错误。
学习 & 练习算法#
算法:搜索、查找、排序、双指针、回溯、分治、动态规划、贪心、位运算、数学。
数据结构:数组、栈、队列、字符串、链表、树、图、堆、哈希表。
LeetCode#
LeetCode 是一个算法学习网站,提供了大量的算法题目,可以用来训练自己的算法能力。
算法竞赛#
算法竞赛是程序员的必修课,也是衡量一个程序员水平的重要标准。
ACM 竞赛
蓝桥杯:蓝桥杯是一个算法竞赛,由蓝桥杯官方举办,主要面向高校学生。
