Quicksort又称划分交换排序,最早由东尼·霍尔提出。在平均状况下,排序n个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n)算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
算法步骤
- 从数列中挑出一个元素,称为 “基准”(pivot),
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
Swift代码
由上面的步骤我们可以写出一个简单的快排算法:
func quickSortSimple(array:[Int]) -> [Int]{ if array.count <= 1 { return array } var lowArray = [Int](), highArray = [Int]() var pivot = array.first! for i in 1...array.count - 1{ let num = array[i] if num > pivot{ highArray.append(num) }else{ lowArray.append(num) } } var result = quickSortSimple(array: lowArray) result.append(pivot) let highResult = quickSortSimple(array: highArray) result.append(contentsOf: highResult) return result }
|
上面简单版本有个缺点,就是它需要Ω(n)的额外存储空间。额外需要的存储器空间配置,在实际上的实现,也会极度影响速度和缓存的性能。有一个比较复杂使用原地(in-place)分区算法的版本如下:
func partition(a: inout [Int],low:Int,high:Int) -> Int{ if low >= high{ return low } let pivot = a[low] var i = low, j = high while i < j { while i < j && a[j] >= pivot { j -= 1 } while i < j && a[i] <= pivot { i += 1 } if i < j { swap(&a[i], &a[j]) } } if i != low{ swap(&a[i], &a[low]) } return i } func quickSort(array: inout [Int], low:Int, high:Int){ if low > high{ return } let index = partition(a: &array, low: low, high: high) quickSort(array: &array, low: low, high: index - 1) quickSort(array: &array, low: index + 1, high: high) }
|