基本思想
在一趟排序过程中,选择一个基准值,通过交换的方式将待排序序列分为两个序列。其中一个序列在基准值左侧,这部分中所有数值都小于基准值;另一序列在基准值右侧,这部分中所有数值都大于基准值。然后对分割出的两个序列重复上述过程,直到排序结束。
详细排序步骤
- 第一步:从待排序序列选取一个基准值(一般为待排序列的首元素)并记录,使得 i , j 分别指向待排序序列首元素和最后一个元素。
- 第二步:从待排序序列右侧开始,比较 j 指向的元素和基准值的大小,如果大于基准值,j 向前移动一个元素,直到找到一个比基准值小的元素,停止 j 的移动。此时, 将 j 所指向的元素赋值给i所指向的元素,并让 i 向后移动一个元素。
- 第三步:从待排序序列左侧开始,比较 i 指向的元素和基准值的大小,如果小于基准值,i 向后移动一个元素,直到找到一个比基准值大的元素,停止 i 的移动。此时, 将 i 所指向的元素赋值给 j 所指向的元素,并让 j 向前移动一个元素。
- 第四步:重复上述步骤,直到 i 和 j 指向同一个元素,然后将基准值赋值给 i 和 j 所指向的元素。此时,序列已经被基准值分为两部分,左侧所有元素小于基准值,右侧所有的元素大于基准值。
- 第五步:使用相同方法,对基准值两侧的序列分别进行排序。
排序示例
假设,对序列{3, 4, 63, 4, 0, 1, 32, -2, 21}进行排序。
第1步:选取基准值为3(序列首元素),此时,i=0,j=8。
第2步:
从序列右侧开始向左逐渐移动 j 直到找到一个比3小的元素即-2,此时i=0, j=7。
然后把 j 指向的元素赋值到 i 所在位置,此时序列变为:{-2, 4, 63, 4, 0, 1, 32, -2, 21}。
第3步:
从序列左侧开始向右逐渐移动 i 直到找到一个比3大的元素即4,此时i=1, j=7。
然后把 i 指向元素赋值到 j 所在位置,此时序列变为:{-2, 4, 63, 4, 0, 1, 32, 4, 21}。
第4步:
从序列右侧开始向左逐渐移动 j 直到找到一个比3小的元素即1, 此时i=1, j=5。
然后把 j 指向元素赋值到 i 所在位置,此时序列变为:{-2, 1, 63, 4, 0, 1, 32, 4, 21}。
第5步:
从序列左侧开始向右逐渐移动 i 直到找到一个比3大的元素即63,此时i=2, j=5。
然后把 i 指向元素赋值到 j 所在位置,此时序列变为:{-2, 1, 63, 4, 0, 63, 32, 4, 21}。
第6步:
从序列右侧开始向左逐渐移动 j 直到找到一个比3小的元素即0, 此时i=2, j=4。
然后把 j 指向元素赋值到 i 所在位置,此时序列变为:{-2, 1, 0, 4, 0, 63, 32, 4, 21}。
第7步:
从序列左侧开始向右逐渐移动 i 直到找到一个比3大的元素即4,此时i=3, j=4。
然后把 i 指向元素赋值到 j 所在位置,此时序列变为:{-2, 1, 0, 4, 4, 63, 32, 4, 21}。
第8步:
从序列右侧开始向左移动 j ,于是i=j=3,将基准值赋值给i(或j)指向位置。序列变化为:{-2, 1, 0, 3, 4, 63, 32, 4, 21}
至此,第一趟排序结束。
此时序列以基准值分为了两部分,即小于基准值3的左侧部分{-2, 1, 0}和大于基准值3的右侧部分{4, 63, 32, 4, 21}。
下面再分别对序列{-2, 1, 0}和{4, 63, 32, 4, 21}按上述方法排序,详细过程不再演示。
说明:序列中加粗的数字为 i 所指向元素,斜体加粗数字为 j 所指向元素,如2 和 -2。
算法特点
时间复杂度
最坏情况下:对于一个有n个元素的序列来说,假如基准值右侧数据均大于(或小于)基准值,那么分割后的序列只有一个且序列长度为n-1,如果每次都发生这种情况,那块快速排序就退化程了冒泡排序,时间复杂度变为了O(n^2)。
理想情况下:
如果每次排序后都能将序列分为相等的两个部分,那么每次只需要对n/2的两个子序列进行排序。
在一个大小为 n 的记录中确定一个记录的位置所需要的时间为O(n)。
若T(n)为对n个记录进行排序所需要的时间,我们可以推导出快速排序的时间复杂度为:
1 | T(n) <= cn + 2T(n/2) c是一个常数 |
其中cn 是一次划分所用的时间,c是一个常数
最坏的情况,每次划分都得到一个子序列,时间复杂度为:
1 | T(n) = cn + T(n-1) |
稳定性
因为快速排序在进行交换时,只是根据比较基数值判断是否交换,且不是相邻元素来交换,在交换过程中可能改变相同元素的顺序,因此是一种不稳定的排序算法。
代码
1 | public static int[] sort(int[] array, int start, int end) { |
代码地址:快速排序源码