算法里的执念与妥协:从快速排序和堆排序聊起

🔑 关键词:排序算法,递归,性能,编程思想,权衡

📖 摘要:一个普通程序员对快速排序和堆排序的反思,以及算法选择背后的生活哲学。

刚学编程那会儿,我最喜欢的排序算法是快速排序。不是因为它多难懂,是因为它快——好像所有面试题都在吹它。那时候我总爱把快排挂在嘴边,觉得写for循环做冒泡特别丢人。有一次我写了一个处理百万条订单数据的脚本,用的快排,结果内存差点爆了。当时我有点懵,后来才明白,快速排序虽然平均性能好,但最坏情况能到O(n²),而且因为递归,栈的深度也是个隐患。那一刻我忽然想,算法题目里的漂亮和工程运行里的真实,完全不是一回事。

图片

后来我仔细对比了堆排序。它走的是另一条路,先把乱糟糟的数组堆成一个大顶堆,然后把顶端的最大值倒腾到末尾,再重新调整。整个过程没有递归,空间占用是常数级别,最坏情况也是O(n log n)。听起来好像比快排更稳。但问题是什么?它不够“性感”。它的常数因子很大,实际的排序速度常常比快排慢。而且堆排序的交换是跳跃式的,对CPU缓存很不友好。你看着它很努力,但就是跑不快。这就像两个性格完全不同的人,一个天才型,状态好时无敌,但容易情绪崩溃;一个踏实型,从不出错,但有点死板。

图片

我有一段时间特别追求完美算法,觉得凡是能找出理论最优解的东西,就该用理论去解决。但后来在实际项目中,我经常用插入排序——一个简单到不好意思拿出来讲的算法。因为当数据量很小或者基本有序时,插入排序的代码量少,没有递归开销,效果反而比那些复杂的算法好。这就好像你明明知道高端料理机很专业,但每次冲一杯速溶咖啡,用一把勺子就能搞定。算法不是越高级越好,而是越合适越好。你首先要搞清楚你的数据长什么样,你的机器有什么限制,你的时间能不能耗得起,你写出来的代码别人能不能维护。

图片

今年我重新想了想快速排序和堆排序这件事。其实谁也不是赢家,它们只是在不同维度上作出了不同的取舍。快排追求平均情况的极致速度,所以它愿意承担最坏情况的险;堆排序追求稳定的上界,所以它放弃了常数因子的效率。没有算法是完美的,你想要的往往是某一个指标优先。以前我总是纠结,一定要分出个高下,现在觉得,这大概就是算法教会我的——接受不完美的存在,然后在不完美里作出自己的选择。可能这就是成年人的世界吧。

图片

🏷️ 标签: