快速排序 (QuickSort)
快速排序:激进的领袖
算法背后的故事:排队的逻辑
想象你正试图在一家嘈杂的派对上,按身高为 100 个人排队。 你可以一个一个比对,但那会耗费一整晚的时间。
1959 年,Tony Hoare 在莫斯科研究机器翻译,需要对俄语单词进行排序。他意识到,如果他随便选一个人——基准点 (Pivot)——并问其他人:“你比这个人矮还是高?”,他就能迅速将人群分为两组。
接着,他告诉“矮个子”组在他们内部进行同样的操作,同时也让“高个子”组这样做。当每一个小小组都排好序时,整个派对就变成了一条完美的直线。
他将其命名为 快速排序 (QuickSort),因为与当时那些缓慢、谨慎的算法相比,它的移动速度快得惊人,具有爆发力。
为什么需要它?
快速排序是几乎所有编程语言(如 C++ 的 sort() 或 Java 对原始类型的排序)的默认选择。
- 速度: 在实践中,由于它与计算机硬件(缓存友好性)的交互方式,它通常比其他 算法更快。
- 原地排序: 它不需要巨大的临时仓库;它直接在原有的阵地上完成排序。
算法是如何“思考”的
这个算法像是一个果断的领袖,通过递归来委派权力。
选拔 (Pivot): 它挑选一个元素作为“标准”。 专家提示: 选到一个极端的基准点(比如最小的那个)会让它变慢。一个好的领袖应该是“平庸/平均”的。
大分裂 (分区): 它横扫整个列表,将所有比基准点小的元素移到左边,大的移到右边。一轮横扫后,基准点就回到了它的终身归宿,永远不需要再移动了。
递归委派: 领袖随后指向左边和右边说:“现在,你们两边按同样的方法处理自己。”
这个过程不断重复,直到每个元素都成了自己的基准点,秩序就此达成。
工程决策:阿基里斯之踵
快速排序很快,但它不稳定。
- 稳定性: 如果你有两个价格相同的“苹果”,快速排序可能会交换它们的相对顺序。如果你需要保持相等元素的原始顺序,请使用 归并排序。
- 最坏情况: 如果你总是选到最差的基准点,速度会退化到 。现代工程师使用“三数取中”或随机基准点来防止这种情况。
实现参考 (Python)
def quicksort(arr):
# 基础情况:单个元素已经是排序好的
if len(arr) <= 1:
return arr
# 1. 选拔:挑选中间的元素作为领袖 (Pivot)
pivot = arr[len(arr) // 2]
# 2. 大分裂
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
# 3. 递归委派
return quicksort(left) + middle + quicksort(right)
# 注意:以上是“美学版”代码。
# 生产环境通常使用“原地交换”版本以节省内存。小结
快速排序教会我们:领导力在于划分。通过做出一个坚定的决策(基准点)并委派剩余任务,我们能在记录时间内解决宏大问题。它提醒我们,有时候,为了让世界团结在一起,你必须先知道如何将它分开。
