1 快速排序划分序列的方法
(1) 第一种
第一种方法,先扫描右边的元素,一旦遇到比t大的元素就将此元素移到最左边,然后再从左边开始,一旦遇到比t大的元素就将此元素放到右边,再从右边…….直到左右交错。最后将t放到划分位置处。
while( l < h){ //Check elements in back while(h > l && a[h] >= t){ --h; } //The back element which less than the key value a[l] = a[h]; while(l < h && a[l] <= t){ ++l; } a[h] = a[l]; } //l = h a[l] = temp; |
(2) 第二种Lomuto划分方案
从左到右扫描,遇见比t小的就和最左大的元素交换,用m作为比t小的最右元素的下标,最后再将t和划分位置(m)元素交换。
m = l -1 for i = [l, h] if a[i] < t // a[i] > t若从大到小排序 swap(++m, i) swap(m, l) //a[l]被选作划分序列的元素即t |
swap()函数用于交换数组中的两个元素。程序中能访问到的a[m]始终是左边最后一个小于于t的数组元素,a[m+1](除a[u+1])是大于t的第一个数组元素。
(3)第三种 Bob Sedgewick划分方案
m = h + 1 for i = [u, l] if a[i] >= t swap(--m, i) |
程序中能访问到的a[m]是大于等于t最左边的一个元素,a[m-1]是小于t的一个元素。故而循环完毕后,不再需要swap语句。这样就比Lomuto少一个语句。
m = i = h + 1 do while a[--i] < t NULL; swap(--m, i) while i != l |
程序中能访问到的a[m]为大于t的最左一个元素。
2 特殊输入时改善快速排序
当待排序列全为相等的元素(某部分为全为相等的序列)和待排序列已经有序时快排的时间复杂度为O(n^2)(当选取第一个元素作为t时)。当序列达到1M时,此时快速排序的时间将会达几个小时,而O(nlogn)处理1M数据是秒级别的。
(1) 排相等元素
此时划分序列的代码需要更改如下:
loop do i++ while i < h && a[i] < t do j-- while a[j] > t if i > j break swap(i, j) swap(i, j) //划分完成 |
(2) 排有序元素
因为当待排序列已经有序时,每次都将序列划分为1 和n-1的序列,如此就需要n此划分。结合函数本身的O(n),造就快排的时间复杂度为O(n^2)。
3 总结
这只是快排的一个例子,它经历了两个层次:
- 算法本身的改进。
- 改进算法面对的特殊情况。
Small Box Note Over。