欢迎来到算法的世界!

你好!今天我们要深入探讨计算机科学中最令人兴奋的部分之一:算法 (Algorithms)。你可能会觉得算法是复杂的数学谜题,但其实它们就只是一组指令,就像烘焙蛋糕的食谱,或者是你系鞋带时遵循的步骤一样。在本章中,我们将学习如何有效地设计这些指令,如何衡量它们有多「快」,并探索每一位优秀的计算机科学家都必须了解的「经典」算法。别担心一开始会觉得负担太重——我们会一步一步把它拆解开来!

1. 设计与分析算法

在我们编写任何代码之前,必须先构思计划。分析与设计 (Analysis and design) 涉及观察一个问题并决定解决它的最佳途径。当我们分析一个算法时,通常会关心两件事:
1. 时间 (Time): 执行需要多久?
2. 空间 (Space): 它占用了多少内存?

类比: 想象你在清理房间。其中一个「算法」是把所有东西都丢进衣橱。这非常快速(时间短),但占用了大量的衣橱空间。另一个「算法」是把每件物品都完美地折叠好。这会花费很多时间,但节省了空间。在计算机科学中,我们总是在寻求这两者之间的最佳平衡点!

重点总结

算法是一组解决问题的步骤。我们根据其执行时间和占用的内存空间来评估它们。

2. 大 O 符号:衡量效率

我们如何告诉朋友一个算法比另一个「更好」?我们使用大 O 符号 (Big O Notation)。这是一种描述算法性能随着数据量(我们称之为 \(n\))增加而如何变化的方法。

大 O 类型快速回顾:
- \(O(1)\) - 常数复杂度 (Constant Complexity): 无论你有多少数据,时间都保持不变。例如:查看列表中的第一项。
- \(O(\log n)\) - 对数复杂度 (Logarithmic Complexity): 时间增长得非常缓慢。这是搜索算法的「黄金标准」。例如:二分搜索法 (Binary Search)。
- \(O(n)\) - 线性复杂度 (Linear Complexity): 如果数据量加倍,所需时间也会加倍。例如:读取列表一次。
- \(O(n^2)\) - 多项式复杂度 (Polynomial Complexity): 通常涉及嵌套循环 (Nested loops)。如果数据量加倍,所需时间会变成原来的四倍!例如:冒泡排序法 (Bubble Sort)。
- \(O(2^n)\) - 指数复杂度 (Exponential Complexity): 每增加一个数据,时间就会加倍。这些算法非常「昂贵」且缓慢。例如:解决某些复杂的谜题。

你知道吗? 即使电脑的速度快上一亿倍,\(O(2^n)\) 的算法最终还是会变得慢到无法使用,因为它的增长速度实在是太惊人了!

重点总结

大 O 衡量的是效率。从最快到最慢,顺序是:\(O(1)\)、\(O(\log n)\)、\(O(n)\)、\(O(n^2)\)、\(O(2^n)\)。

3. 数据结构的算法

存储数据的不同方式(数据结构)需要不同的算法来管理。以下是你必须知道的重要概念:

堆栈 (Stacks) 与队列 (Queues): 我们使用算法来对堆栈进行 Push(加入)或 Pop(移除,类似叠盘子),并对队列进行 Enqueue(加入)或 Dequeue(移除,类似排队购物)。

链表 (Linked Lists): 若要寻找项目,我们从 Head(开头)开始,沿着指针从一个节点走到下一个,直到到达 Tail(结尾)。

树状结构遍历 (Tree Traversals): 为了访问树中的每个节点,我们使用两种主要方法:
1. 广度优先搜索 (Breadth-First): 在移到下一层之前,先访问同一层的所有节点(向「宽度」发展)。
2. 深度优先搜索 (后序遍历,Depth-First, Post-order): 在回溯并尝试另一分支之前,先尽可能深入某一分支(向「深度」发展)。

重点总结

算法是我们用来操作数据结构的「工具」。遍历 (Traversing) 指的是访问结构中的每一个部分一次。

4. 标准排序算法

排序只是将事物按顺序排列(如字母或数字顺序)。你需要知道这四种:

冒泡排序法 (Bubble Sort): 比较两个相邻元素。如果顺序错误就交换它们。重复此过程直到末尾。然后重新开始,直到整个列表排序完成。效率:\(O(n^2)\)。
插入排序法 (Insertion Sort): 每次拿取一个项目,并将其「插入」到已排序子列表的正确位置。想象一下你如何排列手中的扑克牌。效率:\(O(n^2)\)。
合并排序法 (Merge Sort): 一种「分治法 (Divide and Conquer)」算法。重复将列表对半分割,直到你有许多只有 1 个元素的微型列表。然后,以正确的顺序将它们「合并」回来。效率:\(O(n \log n)\)。
快速排序法 (Quick Sort): 选取一个「基准值 (Pivot)」。将所有小于基准值的项目放在左边,大于的放在右边。对左侧和右侧重复此过程。效率:平均为 \(O(n \log n)\)。

常见错误: 许多学生认为冒泡排序法很好,因为它很容易编写。然而,面对大量数据时,它非常缓慢!

重点总结

对于大型列表,合并排序法快速排序法通常比冒泡排序法插入排序法快得多。

5. 搜索与路径搜索算法

我们该如何在干草堆中寻找特定的一根针,或是找出到朋友家最快的路线?

线性搜索 (Linear Search): 从开头开始,逐一检查每个项目。效率:\(O(n)\)。
二分搜索法 (Binary Search): 列表必须先经过排序。查看中间的元素。你的目标项目是更大还是更小?舍弃不需要的那一半,然后重复步骤。效率:\(O(\log n)\)。

戴克斯特拉算法 (Dijkstra’s Shortest Path): 这可以在加权图(如带有距离的地图)中找到两点之间的最短路径。它会持续记录到达每个点的「成本」,并总是选择下一个成本最低的路径。

A* 算法: 这是戴克斯特拉算法的改进版本。它使用一个启发式函数 (Heuristic)(一种基于经验的估计)来将搜索重点导向目标,使其速度快得多。类比:戴克斯特拉算法就像池塘中的涟漪,向四面八方扩散;而 A* 算法则更像是一支指向出口的手电筒。

重点总结

二分搜索法速度很快,但要求数据必须已排序。戴克斯特拉算法能找到绝对最短路径,而 A* 算法则利用额外的「猜想」(启发式函数)来更快地找到路径。

快速复习测验(脑力检查!)

1. \(O(n)\) 和 \(O(n^2)\) 哪一个比较快?
2. **二分搜索法**的主要要求是什么?
3. 哪种排序算法会使用「基准值 (Pivot)」?
4. **A*** 算法中使用的「猜想」名称是什么?

答案:1. \(O(n)\) 比较快。 2. 列表必须已排序。 3. 快速排序法。 4. 启发式函数 (Heuristic)。

做得好!算法可能会很棘手,但只要你记住基本的步骤以及它们如何随数据规模变化(大 O),你就已经在掌握 H446 课程的道路上迈出了一大步!