缘起

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

插入排序

  1. 插入排序是一种从序列左端开始依次对数据进行排序的算法。
  2. 在排序过程中,左侧的数据陆续归位,
  3. 而右侧留下的就是还未被排序的数据。
  4. 插入排序的思路就是从右侧的未排序区域内取出一个数据,
  5. 然后将它插入到已排序区域内合适的位置上。
  6. 时间复杂度和冒泡排序的一样,都为O(n^2)。
  7. 摘自 <<我的第一本算法书>> 【日】石田保辉;宫崎修一

基本流程

  1. 给定待排序数组data[N]
  2. 如果N <= 1, 直接返回
  3. 视data[0]为已排序状态
  4. 从位置1开始, “插入”到左侧已排序区
  5. 类似冒泡排序, 将data[1]与其前方的数字两两比较
  6. 因为左侧是已排序区, 因此遇到首个更小的数字, 本轮比较即可终止, 前面的数字只会更小, 无需比较了.
  7. 循环4-6, 直到右侧数字全部插入左侧区域, 完成排序

改进流程(二分插入)

  1. 由于左侧区域是已排序区域, 因此可以使用二分查找法, 快速定位插入位置
  2. 给定左侧已排序的区域data[0:N]
  3. 现准备插入data[N+1]
  4. 初始状态, 设置left=0, right = N-1
  5. 取位置p = (left + right) / 2
  6. 比较data[p]与data[N+1]
  7. 如果data[p] == data[N+1], 则直接插入位置p
  8. 如果data[p]更大, 说明目标位置还在更左, 则设置right = p - 1
  9. 如果data[p]更小, 说明目标位置还在更右, 则设置left = p + 1
  10. 循环5-9步, 直到left,right收敛为同一位置
  11. 比较data[left]与data[N+1], 小于等于则插入left+1位置, 否则插入left位置
  12. 循环3-11步, 直到右侧数字全部插入左侧区域, 完成排序

目标

  • 实现并验证普通插入排序, 二分插入排序, 并观察两者效率差异
  • 通过定义比较函数兼容任意值类型
  • 通过调整比较函数实现倒序输出

设计

  • ISorter: 定义排序算法接口, 以及值比较函数
  • tInsertSort: 实现插入排序, 通过参数控制, 切换插入函数, 分别实现普通插入和二分插入

单元测试

insert_sort_test.go, 测试过程与选择排序基本一致
从测试结果看, 普通插入排序比选择排序快约30%, 二分插入又比普通插入快近四倍.

  1. package sorting
  2. import (
  3. "fmt"
  4. "learning/gooop/sorting"
  5. "learning/gooop/sorting/insert_sort"
  6. "math/rand"
  7. "testing"
  8. "time"
  9. )
  10. func Test_InsertSort(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. fnTestSorter := func(sorter sorting.ISorter) {
  37. reversed = false
  38. // test simple array
  39. samples := []interface{} { 2,3,1 }
  40. sorter.Sort(samples, fnCompare)
  41. fnAssertTrue(fmt.Sprintf("%v", samples) == "[1 2 3]", "expecting 1,2,3")
  42. t.Log("pass sorting [2 3 1] >> [1 2 3]")
  43. // test 10000 items sorting
  44. rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
  45. sampleCount := 10000
  46. t.Logf("prepare large array with %v items", sampleCount)
  47. samples = make([]interface{}, sampleCount)
  48. for i := 0;i < sampleCount;i++ {
  49. samples[i] = rnd.Intn(sampleCount*10)
  50. }
  51. t.Logf("sorting large array with %v items", sampleCount)
  52. t0 := time.Now().UnixNano()
  53. sorter.Sort(samples, fnCompare)
  54. cost := time.Now().UnixNano() - t0
  55. for i := 1;i < sampleCount;i++ {
  56. fnAssertTrue(fnCompare(samples[i-1], samples[i]) != sorting.GREATER, "expecting <=")
  57. }
  58. t.Logf("end sorting large array, cost = %v ms", cost / 1000000)
  59. // test 0-20
  60. sampleCount = 20
  61. t.Log("sorting 0-20")
  62. samples = make([]interface{}, sampleCount)
  63. for i := 0;i < sampleCount;i++ {
  64. for {
  65. p := rnd.Intn(sampleCount)
  66. if samples[p] == nil {
  67. samples[p] = i
  68. break
  69. }
  70. }
  71. }
  72. t.Logf("unsort = %v", samples)
  73. sorter.Sort(samples, fnCompare)
  74. t.Logf("sorted = %v", samples)
  75. 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")
  76. t.Log("pass sorting 0-20")
  77. // test special
  78. samples = []interface{} {}
  79. sorter.Sort(samples, fnCompare)
  80. t.Log("pass sorting []")
  81. samples = []interface{} { 1 }
  82. sorter.Sort(samples, fnCompare)
  83. t.Log("pass sorting [1]")
  84. samples = []interface{} { 3,1 }
  85. sorter.Sort(samples, fnCompare)
  86. fnAssertTrue(fmt.Sprintf("%v", samples) == "[1 3]", "expecting 1,3")
  87. t.Log("pass sorting [1 3]")
  88. reversed = true
  89. samples = []interface{} { 2, 3,1 }
  90. sorter.Sort(samples, fnCompare)
  91. fnAssertTrue(fmt.Sprintf("%v", samples) == "[3 2 1]", "expecting 3,2,1")
  92. t.Log("pass sorting [3 2 1]")
  93. }
  94. t.Log("\ntesting SimpleInsertSorter")
  95. fnTestSorter(insert_sort.SimpleInsertSorter)
  96. t.Log("\ntesting BinaryInsertSorter")
  97. fnTestSorter(insert_sort.BinaryInsertSorter)
  98. }

测试输出

  1. $ go test -v insert_sort_test.go
  2. === RUN Test_InsertSort
  3. insert_sort_test.go:109:
  4. testing SimpleInsertSorter
  5. insert_sort_test.go:48: pass sorting [2 3 1] >> [1 2 3]
  6. insert_sort_test.go:53: prepare large array with 10000 items
  7. insert_sort_test.go:59: sorting large array with 10000 items
  8. insert_sort_test.go:66: end sorting large array, cost = 188 ms
  9. insert_sort_test.go:70: sorting 0-20
  10. insert_sort_test.go:81: unsort = [10 6 14 18 12 11 19 9 17 8 7 0 15 16 1 3 5 13 4 2]
  11. insert_sort_test.go:84: sorted = [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
  12. insert_sort_test.go:86: pass sorting 0-20
  13. insert_sort_test.go:91: pass sorting []
  14. insert_sort_test.go:95: pass sorting [1]
  15. insert_sort_test.go:100: pass sorting [1 3]
  16. insert_sort_test.go:106: pass sorting [3 2 1]
  17. insert_sort_test.go:112:
  18. testing BinaryInsertSorter
  19. insert_sort_test.go:48: pass sorting [2 3 1] >> [1 2 3]
  20. insert_sort_test.go:53: prepare large array with 10000 items
  21. insert_sort_test.go:59: sorting large array with 10000 items
  22. insert_sort_test.go:66: end sorting large array, cost = 46 ms
  23. insert_sort_test.go:70: sorting 0-20
  24. insert_sort_test.go:81: unsort = [3 19 13 17 11 4 15 10 14 5 6 0 7 2 8 16 18 12 1 9]
  25. insert_sort_test.go:84: sorted = [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
  26. insert_sort_test.go:86: pass sorting 0-20
  27. insert_sort_test.go:91: pass sorting []
  28. insert_sort_test.go:95: pass sorting [1]
  29. insert_sort_test.go:100: pass sorting [1 3]
  30. insert_sort_test.go:106: pass sorting [3 2 1]
  31. --- PASS: Test_InsertSort (0.24s)
  32. PASS
  33. ok command-line-arguments 0.237s

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

tInsertSort.go

实现插入排序, 通过参数控制, 切换插入函数, 分别实现普通插入和二分插入

  1. package insert_sort
  2. import (
  3. "learning/gooop/sorting"
  4. )
  5. type tInsertSort struct {
  6. fnInsert fnInsertFunction
  7. }
  8. func newInsertSort(usingBinarySearch bool) sorting.ISorter {
  9. it := &tInsertSort{
  10. nil,
  11. }
  12. if usingBinarySearch {
  13. it.fnInsert = binaryInsert
  14. } else {
  15. it.fnInsert = simpleInsert
  16. }
  17. return it
  18. }
  19. type fnInsertFunction func(data []interface{}, from int, to int, comparator sorting.CompareFunction)
  20. // 插入排序
  21. func (me *tInsertSort) Sort(data []interface{}, comparator sorting.CompareFunction) []interface{} {
  22. if data == nil {
  23. return nil
  24. }
  25. size := len(data)
  26. if size <= 1 {
  27. return data
  28. }
  29. for i := 1;i < size;i++ {
  30. me.fnInsert(data, 0, i, comparator)
  31. }
  32. return data
  33. }
  34. // 将位于to的值插入到有序序列data的[from,to)位置
  35. func simpleInsert(data []interface{}, from int, to int, comparator sorting.CompareFunction) {
  36. for i := to;i>from;i-- {
  37. prev := i - 1
  38. if comparator(data[prev], data[i]) == sorting.GREATER {
  39. data[prev], data[i] = data[i], data[prev]
  40. } else {
  41. break
  42. }
  43. }
  44. }
  45. // 将位于to的值插入到有序序列data的[from,to)位置
  46. func binaryInsert(data []interface{}, from int, to int, comparator sorting.CompareFunction) {
  47. p := binaryIndexOf(data, from, to - 1, data[to], comparator)
  48. if p != to {
  49. v := data[to]
  50. moveArrayRange(data, p, p + 1, to - p)
  51. data[p] = v
  52. }
  53. }
  54. // 整体移动数组的指定范围
  55. func moveArrayRange(data []interface{}, src int, dst int, size int) {
  56. t := dst + size - 1
  57. for i := src + size - 1;i >= src;i-- {
  58. data[t] = data[i]
  59. t--
  60. }
  61. }
  62. // 二分查找首个>=v的下标
  63. func binaryIndexOf(data []interface{}, from int, toInclusive int, v interface{}, comparator sorting.CompareFunction) int {
  64. for {
  65. if from == toInclusive {
  66. switch comparator(data[from], v) {
  67. case sorting.LESS:
  68. return from + 1
  69. default:
  70. return from
  71. }
  72. } else {
  73. p := (from + toInclusive) / 2
  74. switch comparator(data[p], v) {
  75. case sorting.LESS:
  76. from = min(p + 1, toInclusive)
  77. break
  78. case sorting.EQUAL:
  79. return p
  80. case sorting.GREATER:
  81. toInclusive = max(from, p - 1)
  82. break
  83. }
  84. }
  85. }
  86. }
  87. func min(a, b int) int {
  88. if a <= b {
  89. return a
  90. } else {
  91. return b
  92. }
  93. }
  94. func max(a, b int) int {
  95. if a >= b {
  96. return a
  97. } else {
  98. return b
  99. }
  100. }
  101. var SimpleInsertSorter = newInsertSort(false)
  102. var BinaryInsertSorter = newInsertSort(true)

(end)