基础算法(快速排序)

Quicksort又称划分交换排序,最早由东尼·霍尔提出。在平均状况下,排序n个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n)算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

算法步骤

  1. 从数列中挑出一个元素,称为 “基准”(pivot),
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归地(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)
}