Appearance
什么是算法
想象你第一次去一个陌生的城市,需要从火车站到酒店。你会怎么做?
你可能会打开手机地图,输入目的地,然后按照导航一步步走。导航软件告诉你:向前走 200 米,左转,直行 500 米,右转……这一系列明确的、有顺序的指令,其实就是一个「算法」。
算法并不神秘。它就是解决问题的步骤。
算法的定义
我们先给出一个通俗的定义:
算法是解决特定问题的一系列明确步骤。
如果要更正式一点,可以这样描述:
算法是一个有限的、明确的、有效的指令序列,用于解决某一类问题或执行某个计算任务。
可以用一个简单的模型来理解:
┌─────────┐ ┌─────────────┐ ┌─────────┐
│ 输入 │ → │ 算法处理 │ → │ 输出 │
│ (Input) │ │ (Algorithm) │ │ (Output)│
└─────────┘ └─────────────┘ └─────────┘比如「在一组数中找最大值」这个问题:
- 输入:一组数字
[3, 7, 2, 9, 5] - 算法:逐个比较,记录当前最大值
- 输出:最大值
9
算法的五个特性
一个合格的算法必须具备以下五个特性。这不是什么高深的理论,而是非常朴素的要求。
1. 有穷性(Finiteness)
算法必须在有限步骤内终止。
这个要求看起来理所当然,但在实际编程中,无限循环是一个常见的 bug。
javascript
// ✅ 正例:有穷的算法
function findMax(arr) {
let max = arr[0];
for (let i = 1; i < arr.length; i++) { // 有限次循环
if (arr[i] > max) {
max = arr[i];
}
}
return max; // 必定终止
}
// ❌ 反例:无穷循环(不是算法)
function infiniteLoop() {
while (true) { // 永不终止
console.log("running...");
}
}2. 确定性(Definiteness)
每一步都有明确的含义,不能有歧义。
「差不多」「大概」这种模糊的描述不能出现在算法中。
javascript
// ✅ 正例:明确的指令
if (a > b) {
return a;
}
// ❌ 反例:模糊的描述(伪代码)
// "如果 a 比 b 大一些,返回 a"
// 什么叫"大一些"?这就是歧义3. 可行性(Feasibility)
每一步都能在有限时间内完成。
不能要求执行者做不可能完成的事情。
javascript
// ✅ 正例:可行的操作
const sum = a + b; // 加法可以在有限时间完成
// ❌ 反例:不可行的操作(伪代码)
// "穷举所有实数"——实数有无穷多个,无法完成4. 输入(Input)
有零个或多个输入。
是的,可以是零个。比如生成一个固定的随机数序列,不需要任何输入。
javascript
// 有输入的算法
function add(a, b) {
return a + b;
}
// 无输入的算法
function getCurrentTime() {
return Date.now();
}5. 输出(Output)
有一个或多个输出。
这是必须的。一个没有输出的算法是没有意义的——你执行了一堆操作,却不告诉别人结果,有什么用呢?
javascript
// ✅ 正例:有输出
function square(n) {
return n * n; // 返回计算结果
}
// ❌ 反例:无输出(虽然语法正确,但作为算法没意义)
function doNothing(n) {
const result = n * n;
// 没有 return,调用者得不到任何结果
}算法与程序的关系
很多初学者会把算法和程序混为一谈。它们有联系,但不是一回事。
| 对比项 | 算法 | 程序 |
|---|---|---|
| 本质 | 解决问题的思想和步骤 | 算法的具体实现 |
| 语言 | 可以用自然语言、伪代码、流程图描述 | 必须用编程语言编写 |
| 依赖 | 与编程语言无关 | 依赖特定的编程语言和平台 |
| 有穷性 | 必须在有限步内终止 | 可以是常驻运行的(如服务器) |
一个关键的认知是:先有算法,后有程序。
当你面对一个编程问题时,正确的流程是:
- 理解问题
- 设计算法(想清楚步骤)
- 用代码实现算法
很多人直接跳到第 3 步,上来就写代码,写着写着发现思路不对,又推倒重来。这就是没有「算法思维」的表现。
同样的算法,可以用不同的语言实现。比如「冒泡排序」这个算法,你可以用 JavaScript 写,也可以用 Python 写,甚至可以用 C 写。语言不同,但算法思想是一样的。
javascript
// JavaScript 版本
function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
return arr;
}python
# Python 版本
def bubble_sort(arr):
for i in range(len(arr) - 1):
for j in range(len(arr) - 1 - i):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr两段代码做的事情完全一样,因为它们实现的是同一个算法。
为什么学习算法
你可能会问:现在有那么多现成的库和框架,为什么还要学习算法?
1. 面试必备
这是最现实的原因。几乎所有大厂的技术面试都会考察算法。LeetCode 刷题已经成为求职的「标准动作」。
2. 培养解决问题的能力
算法不仅仅是「背题」。学习算法的过程,是在训练你系统化思考问题的能力。这种能力在任何技术领域都是通用的。
3. 性能优化
同样的问题,不同的算法可能有天壤之别的效率。
举个例子:在 100 万个数中查找一个数。
- 线性查找:最坏情况需要比较 100 万次
- 二分查找(前提是数组有序):最多只需要比较 20 次
两者的差距是 5 万倍。当数据量更大时,差距会更加惊人。
4. 职业发展
初级程序员写代码,高级程序员设计算法。如果你想突破「码农」的天花板,算法能力是绕不过去的。
本章小结
本章我们认识了算法的基本概念:
- 算法是解决特定问题的一系列明确步骤
- 算法必须具备五个特性:有穷性、确定性、可行性、输入、输出
- 算法是思想,程序是实现——先想清楚步骤,再写代码
- 学习算法能帮助你通过面试、培养思维、优化性能、提升职业竞争力
既然算法有好坏之分,那么如何衡量一个算法的效率呢?这就是下一章要讨论的内容——时间复杂度分析。