快速排序是一个不稳定的排序算法,本文将着重分析该算法不稳定的原因所在。
首先,考虑下图中对一个关键字都为 1 的序列所进行的交换操作。
如图,交换前,枢纽元已放置在最后一个位置。图中,当遇到和枢纽元大小相同的元素时,游标 选择继续前进,直到与游标 相遇为止,然后将枢纽元与游标 和 所指向的元素交换, 再递归的对左右两边的数组进行快速排序,这是快速排序的思想。
但问题也出现了。如图,一轮递归结束后,左右两边的数组明显失衡,可想而知,左边数组仅仅是一个长度与当前数组相当的子数组,右边的数组长度为1,递归算法的复杂度将上升为 ,这触犯了快速排序的 “大忌” !
程序恶化的原因: 当游标所指向的元素同枢纽元相等时选择了继续前进,而不是停留在原地,这就导致游标肆无忌惮的一路向前,根本没有顾及到左右两个数组是否均衡,因而触犯了“大忌”。
避免踩坑: 当游标所指向的元素同枢纽元相等时,待在原地不动,左右游标指向的元素进行交换,再各自向前移动。
下图是没有踩坑的操作示意。
据图,我们似乎做了很多没有必要的交换。但这样做使得最终的枢纽元出现在数组的中间位置上,左右数组呈均衡状态,理论上时间复杂度将是 ,快速排序的性能是最优的,避免了我们陷入复杂度为 的坑。
本文的例子在实际当中很少出现,但当一个序列中存在大量重复的元素,此时如果选择游标继续前进的策略,无疑也会陷入性能上的低谷。
结论: 根据上面的讨论,快速排序出于性能上的考虑,牺牲了算法的稳定性。虽然可以改变交换规则,使得算法保持稳定性,但却牺牲了性能,对于一个排序算法而言,这是得不偿失的,除非是上下文有稳定性方面的需求,否则,不建议改变交换规则。