缘起

最近阅读<<我的第一本算法书>>(【日】石田保辉;宫崎修一)
本系列笔记拟采用golang练习之

选择排序

  1. 选择排序就是重复“从待排序的数据中寻找最小值,
  2. 将其与序列最左边的数字进行交换”这一操作的算法。
  3. 在序列中寻找最小值时使用的是线性查找。
  4. 选择排序的时间复杂度也和冒泡排序的一样,都为O(n^2)。
  5. 摘自 <<我的第一本算法书>> 【日】石田保辉;宫崎修一

流程

  1. 给定待排序数组data[N]
  2. 设定目标位置i = 0
  3. 获取n = min(data, i), 即[0-N)区间的最小值的下标
  4. 判断n是否等于i, 否则交换n, i的数值, 也就是把最小值放到i的位置
  5. i++
  6. 循环3-5步, 直到i == N-1

    目标

  • 实现并测试选择排序
  • 通过定义比较函数兼容任意值类型
  • 通过调整比较函数实现倒序输出

设计

  • ISorter: 定义排序算法接口, 以及值比较函数
  • tSelectSorter: 选择排序的实现. 内部定义min函数, 线性查找数组中指定区间的最小值的下标.

单元测试

select_sort_test.go, 测试过程与冒泡排序基本一致. 从测试结果看, 比冒泡排序快近一倍

  1. package sorting
  2. import (
  3. "fmt"
  4. "learning/gooop/sorting"
  5. "learning/gooop/sorting/select_sort"
  6. "math/rand"
  7. "testing"
  8. "time"
  9. )
  10. func Test_SelectSort(t *testing.T) {
  11. fnAssertTrue := func(b bool, msg string) {
  12. if !b {
  13. t.Fatal(msg)
  14. }
  15. }
  16. reversed := false
  17. fnCompare := func(a interface{}, b interface{}) sorting.CompareResult {
  18. i1 := a.(int)
  19. i2 := b.(int)
  20. if i1 < i2 {
  21. if reversed {
  22. return sorting.GREATER
  23. } else {
  24. return sorting.LESS
  25. }
  26. } else if i1 == i2 {
  27. return sorting.EQUAL
  28. } else {
  29. if reversed {
  30. return sorting.LESS
  31. } else {
  32. return sorting.GREATER
  33. }
  34. }
  35. }
  36. sorter := select_sort.SelectSorter
  37. // test 10000 items sorting
  38. rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
  39. sampleCount := 10000
  40. t.Logf("prepare large array with %v items", sampleCount)
  41. samples := make([]interface{}, sampleCount)
  42. for i := 0;i < sampleCount;i++ {
  43. samples[i] = rnd.Intn(sampleCount*10)
  44. }
  45. t.Log("sorting large array")
  46. t0 := time.Now().UnixNano()
  47. sorter.Sort(samples, fnCompare)
  48. cost := time.Now().UnixNano() - t0
  49. for i := 1;i < sampleCount;i++ {
  50. fnAssertTrue(fnCompare(samples[i-1], samples[i]) != sorting.GREATER, "expecting <=")
  51. }
  52. t.Logf("end sorting large array, cost = %v ms", cost / 1000000)
  53. // test 0-20
  54. sampleCount = 20
  55. t.Log("sorting 0-20")
  56. samples = make([]interface{}, sampleCount)
  57. for i := 0;i < sampleCount;i++ {
  58. for {
  59. p := rnd.Intn(sampleCount)
  60. if samples[p] == nil {
  61. samples[p] = i
  62. break
  63. }
  64. }
  65. }
  66. t.Logf("unsort = %v", samples)
  67. sorter.Sort(samples, fnCompare)
  68. t.Logf("sorted = %v", samples)
  69. fnAssertTrue(fmt.Sprintf("%v", samples) == "[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]", "expecting 0-20")
  70. t.Log("pass sorting 0-20")
  71. // test special
  72. samples = []interface{} {}
  73. sorter.Sort(samples, fnCompare)
  74. t.Log("pass sorting []")
  75. samples = []interface{} { 1 }
  76. sorter.Sort(samples, fnCompare)
  77. t.Log("pass sorting [1]")
  78. samples = []interface{} { 3,1 }
  79. sorter.Sort(samples, fnCompare)
  80. fnAssertTrue(fmt.Sprintf("%v", samples) == "[1 3]", "expecting 1,3")
  81. t.Log("pass sorting [1 3]")
  82. samples = []interface{} { 2,3,1 }
  83. sorter.Sort(samples, fnCompare)
  84. fnAssertTrue(fmt.Sprintf("%v", samples) == "[1 2 3]", "expecting 1,2,3")
  85. t.Log("pass sorting [1 2 3]")
  86. reversed = true
  87. sorter.Sort(samples, fnCompare)
  88. fnAssertTrue(fmt.Sprintf("%v", samples) == "[3 2 1]", "expecting 3,2,1")
  89. t.Log("pass sorting [3 2 1]")
  90. }

测试输出

从测试结果看, 比冒泡排序快近一倍

  1. $ go test -v select_sort_test.go
  2. === RUN Test_SelectSort
  3. select_sort_test.go:46: prepare large array with 10000 items
  4. select_sort_test.go:52: sorting large array
  5. select_sort_test.go:59: end sorting large array, cost = 293 ms
  6. select_sort_test.go:63: sorting 0-20
  7. select_sort_test.go:74: unsort = [19 6 15 14 16 2 5 11 12 18 8 3 17 4 10 7 1 13 9 0]
  8. select_sort_test.go:77: sorted = [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
  9. select_sort_test.go:79: pass sorting 0-20
  10. select_sort_test.go:84: pass sorting []
  11. select_sort_test.go:88: pass sorting [1]
  12. select_sort_test.go:93: pass sorting [1 3]
  13. select_sort_test.go:98: pass sorting [1 2 3]
  14. select_sort_test.go:103: pass sorting [3 2 1]
  15. --- PASS: Test_SelectSort (0.29s)
  16. PASS
  17. ok command-line-arguments 0.297s

ISorter.go

定义排序算法接口, 以及值比较函数

  1. package sorting
  2. type ISorter interface {
  3. Sort(data []interface{}, comparator CompareFunction) []interface{}
  4. }
  5. type CompareFunction func(a interface{}, b interface{}) CompareResult
  6. type CompareResult int
  7. const LESS CompareResult = -1
  8. const EQUAL CompareResult = 0
  9. const GREATER CompareResult = 1

tSelectSorter.go

选择排序的实现. 内部定义min函数, 线性查找数组中指定区间的最小值的下标.

  1. package select_sort
  2. import "learning/gooop/sorting"
  3. type tSelectSorter struct {
  4. }
  5. func newSelectSorter() sorting.ISorter {
  6. return &tSelectSorter{}
  7. }
  8. // 选择排序
  9. func (me *tSelectSorter) Sort(data []interface{}, comparator sorting.CompareFunction) []interface{} {
  10. if data == nil {
  11. return nil
  12. }
  13. size := len(data)
  14. if size <= 1 {
  15. return data
  16. }
  17. last := size - 1
  18. for i := 0;i < last;i++ {
  19. p := me.min(data, i, size, comparator)
  20. if p != i {
  21. data[i], data[p] = data[p], data[i]
  22. }
  23. }
  24. return data
  25. }
  26. // 使用线性查找法, 获取最小值的索引
  27. func (me *tSelectSorter) min(data []interface{}, from int, to int, comparator sorting.CompareFunction) int {
  28. p := from
  29. v := data[from]
  30. for i := from + 1;i < to;i++ {
  31. v1 := data[i]
  32. if comparator(v1, v) == sorting.LESS {
  33. p = i
  34. v = v1
  35. }
  36. }
  37. return p
  38. }
  39. var SelectSorter = newSelectSorter()

(end)