算法为什么重要:一个 Push-Swap 排序项目带来的认识
学编程之初,很多人以为大多数问题"只要写够代码"就能解决。后来才意识到:最难的往往不是写代码,而是决定代码该做什么——这正是算法登场的地方。作者在做一个排序项目(Push-Swap:用两个栈和一组受限操作把数字排好序)时,被迫认真思考不同的排序方式、它们如何扩展、以及在严格约束下如何工作。他真正想分享的是背后的算法思维。
算法是什么
算法是解决问题的分步流程。给 5 2 8 1 3,要得到 1 2 3 5 8,原理上直来直去,但路径很多:反复找最小值放到前面;比较相邻元素并交换;把问题分成小块再合并结果;处理每个数的数字/位而不是比较整体值。结果都一样,有趣的是怎么到达——这个差别对性能有真实后果。
同一问题,非常不同的解法
以冒泡排序为例:反复比较相邻元素、顺序不对就交换。5 2 8 1 一趟后是 2 5 1 8,重复到没有需要交换的为止。它能工作,但最坏时间复杂度 O(n²),n 一长大就贵得飞快。于是引出一个真正的开发问题:两个算法解同一个问题,选哪个?
复杂度改变一切
设算法 A 跑 O(n²)、算法 B 跑 O(n log n)。小输入时差别无所谓;n=1,000 时:n² = 1,000,000,而 n log₂n ≈ 10,000——工作量差距巨大。这就是理解时间复杂度的原因:正确的代码,如果不扩展,仍然是糟糕的解法。
Big O 不是精确秒数
常见的误解是 Big O 告诉你算法精确耗时。它不是——它描述工作量如何随输入规模增长。
- O(1) 常数时间:工作量不随输入增长(
value := numbers[0]) - O(n) 线性:输入翻倍,工作量大致翻倍
- O(n²) 平方:输入翻倍,工作量约翻四倍(双重循环)
- O(log n) 对数:二分查找是经典——不是检查每个元素,而是每步消掉一半搜索空间(1,000,000 → 500,000 → 250,000 → … → 1)
核心思想:不只是推进,而是消除不必要的工作。
选择排序:简单但昂贵
选择排序反复找最小元素放到位。对 5 个元素要做 4 + 3 + 2 + 1 = 10 次比较;n 个元素就是 n(n-1)/2 次——即使每次都提前退出循环,它仍然是 O(n²)(把"最好情况 1 次比较"与"最坏 n-1 次"平均,总次数按 n 增长仍是平方级)。作为对比,归并排序把问题递归分半再合并,O(n log n),n 大时优势巨大。
实践建议
- 动手实现前先写出复杂度:O(n²) 对小输入无所谓、对"线上会涨"的数据就是事故;
- Push-Swap 这类受限操作题的价值在约束——它逼你想"哪些操作是可用的杠杆",而不是直接上排序库;
- 背复杂度表不如背一句话:在问题规模增长前,先问你的解法会怎样增长。