我正在查看Go
,并试图找到经典算法的惯用实现,以了解语言。
我之所以选择quicksort,是因为我对数组与切片,就地与复制交易特别感兴趣。在我解决了一些概念后,我想写一个平行的impl。
有人可以在Go
中向我展示一个惯用的快速排序实现吗?
答案 0 :(得分:15)
好吧,我最终得到了这个。我不知道Go
说它是惯用的,但我使用了切片,单行交换和range
子句。这对我来说非常有用,所以我想我应该分享。
func qsort(a []int) []int {
if len(a) < 2 { return a }
left, right := 0, len(a) - 1
// Pick a pivot
pivotIndex := rand.Int() % len(a)
// Move the pivot to the right
a[pivotIndex], a[right] = a[right], a[pivotIndex]
// Pile elements smaller than the pivot on the left
for i := range a {
if a[i] < a[right] {
a[i], a[left] = a[left], a[i]
left++
}
}
// Place the pivot after the last smaller element
a[left], a[right] = a[right], a[left]
// Go down the rabbit hole
qsort(a[:left])
qsort(a[left + 1:])
return a
}
答案 1 :(得分:3)
查看标准库中sort package的来源,特别是sort.Sort。
答案 2 :(得分:1)
我现在正在学习,现在决定将qsort作为一个简单的任务实现,使用通道和并行,因为在qsort中你可以在旋转数组后同时对两半进行排序。我最终得到了这样的结果:
package main
import (
"fmt"
"math/rand"
"time"
)
func qsort_pass(arr []int, done chan int) []int{
if len(arr) < 2 {
done <- len(arr)
return arr
}
pivot := arr[0]
i, j := 1, len(arr)-1
for i != j {
for arr[i] < pivot && i!=j{
i++
}
for arr[j] >= pivot && i!=j{
j--
}
if arr[i] > arr[j] {
arr[i], arr[j] = arr[j], arr[i]
}
}
if arr[j] >= pivot {
j--
}
arr[0], arr[j] = arr[j], arr[0]
done <- 1;
go qsort_pass(arr[:j], done)
go qsort_pass(arr[j+1:], done)
return arr
}
func qsort(arr []int) []int {
done := make(chan int)
defer func() {
close(done)
}()
go qsort_pass(arr[:], done)
rslt := len(arr)
for rslt > 0 {
rslt -= <-done;
}
return arr
}
func main() {
fmt.Println("About to sort.")
rand.Seed(time.Now().UTC().UnixNano())
arr_rand := make([]int, 20)
for i := range arr_rand {
arr_rand[i] = rand.Intn(10)
}
fmt.Println(arr_rand)
qsort(arr_rand)
fmt.Println(arr_rand)
}
这里有很大的改进空间,例如使用go-routines池而不是为每个分区生成一个新的go-routine,如果len(arr)足够小或者使用[]以外的东西,则使用插入排序进行排序INT。但一般来说,这似乎是一个好的开始。
答案 3 :(得分:0)
简单地从一种语言(例如C语言)中获取代码,并简单地将其转换为另一种语言,例如Go,很少会产生惯用代码。
转到sort package - sort.c源文件
func quickSort(data Interface, a, b, maxDepth int) { // . . . // Avoiding recursion on the larger subproblem guarantees // a stack depth of at most lg(b-a). // . . . }
这个评论是一个线索,实现递归解决方案可能不是最好的策略。 Go使用短筹码。
这是一个迭代的Quicksort解决方案。
package main
import (
"fmt"
"math/rand"
"time"
)
type Item int
type Items []Item
// Algorithms and Data Structures, N. Wirth
// http://www.ethoberon.ethz.ch/WirthPubl/AD.pdf
// 2.3.3 Partition Sort, Quicksort, NonRecursiveQuickSort.
func qSort(a Items) {
const M = 12
var i, j, l, r int
var x Item
var low, high = make([]int, 0, M), make([]int, 0, M)
low, high = append(low, 0), append(high, len(a)-1)
for { // (*take top request from stack*)
l, low = low[len(low)-1], low[:len(low)-1]
r, high = high[len(high)-1], high[:len(high)-1]
for { // (*partition a[l] ... a[r]*)
i, j = l, r
x = a[l+(r-l)/2]
for {
for ; a[i] < x; i++ {
}
for ; x < a[j]; j-- {
}
if i <= j {
a[i], a[j] = a[j], a[i]
i++
j--
}
if i > j {
break
}
}
if i < r { // (*stack request to sort right partition*)
low, high = append(low, i), append(high, r)
}
r = j // (*now l and r delimit the left partition*)
if l >= r {
break
}
}
if len(low)+len(high) == 0 {
break
}
}
}
func main() {
nItems := 4096
a := make(Items, nItems)
rand.Seed(time.Now().UnixNano())
for i := range a {
a[i] = Item(rand.Int31())
}
qSort(a)
for i := range a[1:] {
if a[i] > a[i+1] {
fmt.Println("(* sort error *)")
}
}
}
显然,还有很多工作要做。例如,改进分区算法并将qsort
函数签名更改为使用Go interface
类型。
答案 4 :(得分:0)
对此可以有一些想法。
func quickSort(arr []int) []int {
sort(arr, 0, len(arr) - 1)
return arr
}
func sort(arr []int, low, high int) {
if low < high {
pi := partition(arr, low, high)
sort(arr, low, pi-1)
sort(arr, pi+1, high)
}
}
func partition(arr []int, low, high int) int {
pivot := arr[high]
i := low-1
for j := low; j < high; j++ {
if arr[j] <= pivot {
i++
arr[i], arr[j] = arr[j], arr[i]
}
}
arr[i+1], arr[high] = arr[high], arr[i+1]
return i+1
}