首先你需要知道,**原地算法 (in-place)**的定义:就是特指空间复杂度为 O(1) 的算法;即在算法运行时,输入数据会被输出覆盖。
你还要知道,分而治之 (divide and conquer),基于多项分支递归的算法范式。首先你需要确定何时函数将不再调用自己,再缩小问题规模使其符合前一步。
分
当数组中只有唯一或为空元素时,那它就是有序的,这就是函数停止调用自己的条件。
而其它的调用中,你都必须对数组进行切分,这才能满足上述条件。
func separateSort(a []int, start, end int) {
if start >= end {
return
}
pivot := partition(a, start, end)
// fmt.Println("the pivot is:", a[pivot])
separateSort(a, start, pivot-1)
separateSort(a, pivot+1, end)
}
根据要求你得到了第一个函数,接受一个数组以及它的起始下标,当数组元素唯一时返回。
pivot 基准,或可称为分区点,由 partition 函数的返回值赋值;此时将数组 a 已分为三部分,分区点左右继续递归调用。
治
partition 函数首先对接受到的数组假设,设其末端为分区点;再将小于分区点的元素换到右边,大于分区点的元素换到右边;最后返回结果分区点。
func partition(a []int, start, end int) int {
left, right := start, end
pivot := end
for left < right {
if a[left] > a[pivot] {
if a[right] < a[pivot] {
a[left], a[right] = a[right], a[left]
} else {
right--
}
} else {
left++
}
}
if a[left] != a[pivot] {
a[left], a[pivot] = a[pivot], a[left]
}
// fmt.Printf("array order is: %d --> ", a)
return left
}
最后就是调用了,
func qsort(a []int) {
separateSort(a, 0, len(a)-1)
}
func main() {
a := []int{6, 2, 5, 9, 3, 7, 0, 1, 3, 4, 8}
fmt.Println("Original: ", a)
qsort(a)
fmt.Println("Complete! ", a)
}
靠着代码的输出,你可以看看 pviot 的选择过程:
Original: [6 2 5 9 3 7 0 1 3 4 8]
array order is: [6 2 5 4 3 7 0 1 3 8 9] --> the pivot is: 8
array order is: [1 2 0 3 3 7 5 6 4 8 9] --> the pivot is: 3
array order is: [0 2 1 3 3 7 5 6 4 8 9] --> the pivot is: 0
array order is: [0 1 2 3 3 7 5 6 4 8 9] --> the pivot is: 1
array order is: [0 1 2 3 3 4 5 6 7 8 9] --> the pivot is: 4
array order is: [0 1 2 3 3 4 5 6 7 8 9] --> the pivot is: 7
array order is: [0 1 2 3 3 4 5 6 7 8 9] --> the pivot is: 6
Complete! [0 1 2 3 3 4 5 6 7 8 9]
具体代码执行过程,如下手绘图所示:
在图中依照时间顺序,从上至下可依次寻找相应的 pivot 值。你只需要着眼于一次能够正确完成,那么你只需要在正确地传递数组相应的边界条件即可。