手撸golang 基本数据结构与算法 选择排序

Posted ioly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手撸golang 基本数据结构与算法 选择排序相关的知识,希望对你有一定的参考价值。

缘起

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

选择排序

选择排序就是重复“从待排序的数据中寻找最小值,
将其与序列最左边的数字进行交换”这一操作的算法。
在序列中寻找最小值时使用的是线性查找。

选择排序的时间复杂度也和冒泡排序的一样,都为O(n^2)。

摘自 <<我的第一本算法书>> 【日】石田保辉;宫崎修一

流程

  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, 测试过程与冒泡排序基本一致. 从测试结果看, 比冒泡排序快近一倍

package sorting

import (
    "fmt"
    "learning/gooop/sorting"
    "learning/gooop/sorting/select_sort"
    "math/rand"
    "testing"
    "time"
)

func Test_SelectSort(t *testing.T) {
    fnAssertTrue := func(b bool, msg string) {
        if !b {
            t.Fatal(msg)
        }
    }

    reversed := false
    fnCompare := func(a interface{}, b interface{}) sorting.CompareResult {
        i1 := a.(int)
        i2 := b.(int)

        if i1 < i2 {
            if reversed {
                return sorting.GREATER
            } else {
                return sorting.LESS
            }
        } else if i1 == i2 {
            return sorting.EQUAL
        } else {
            if reversed {
                return sorting.LESS
            } else {
                return sorting.GREATER
            }
        }
    }

    sorter := select_sort.SelectSorter

    // test 10000 items sorting
    rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
    sampleCount := 10000
    t.Logf("prepare large array with %v items", sampleCount)
    samples := make([]interface{}, sampleCount)
    for i := 0;i < sampleCount;i++ {
        samples[i] = rnd.Intn(sampleCount*10)
    }

    t.Log("sorting large array")
    t0 := time.Now().UnixNano()
    sorter.Sort(samples, fnCompare)
    cost := time.Now().UnixNano() - t0
    for i := 1;i < sampleCount;i++ {
        fnAssertTrue(fnCompare(samples[i-1], samples[i]) != sorting.GREATER, "expecting <=")
    }
    t.Logf("end sorting large array, cost = %v ms", cost / 1000000)

    // test 0-20
    sampleCount = 20
    t.Log("sorting 0-20")
    samples = make([]interface{}, sampleCount)
    for i := 0;i < sampleCount;i++ {
        for {
            p := rnd.Intn(sampleCount)
            if samples[p] == nil {
                samples[p] = i
                break
            }
        }
    }
    t.Logf("unsort = %v", samples)

    sorter.Sort(samples, fnCompare)
    t.Logf("sorted = %v", samples)
    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")
    t.Log("pass sorting 0-20")

    // test special
    samples = []interface{} {}
    sorter.Sort(samples, fnCompare)
    t.Log("pass sorting []")

    samples = []interface{} { 1 }
    sorter.Sort(samples, fnCompare)
    t.Log("pass sorting [1]")

    samples = []interface{} { 3,1 }
    sorter.Sort(samples, fnCompare)
    fnAssertTrue(fmt.Sprintf("%v", samples) == "[1 3]",  "expecting 1,3")
    t.Log("pass sorting [1 3]")

    samples = []interface{} { 2,3,1 }
    sorter.Sort(samples, fnCompare)
    fnAssertTrue(fmt.Sprintf("%v", samples) == "[1 2 3]",  "expecting 1,2,3")
    t.Log("pass sorting [1 2 3]")

    reversed = true
    sorter.Sort(samples, fnCompare)
    fnAssertTrue(fmt.Sprintf("%v", samples) == "[3 2 1]",  "expecting 3,2,1")
    t.Log("pass sorting [3 2 1]")
}

测试输出

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

$ go test -v select_sort_test.go 
=== RUN   Test_SelectSort
    select_sort_test.go:46: prepare large array with 10000 items
    select_sort_test.go:52: sorting large array
    select_sort_test.go:59: end sorting large array, cost = 293 ms
    select_sort_test.go:63: sorting 0-20
    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]
    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]
    select_sort_test.go:79: pass sorting 0-20
    select_sort_test.go:84: pass sorting []
    select_sort_test.go:88: pass sorting [1]
    select_sort_test.go:93: pass sorting [1 3]
    select_sort_test.go:98: pass sorting [1 2 3]
    select_sort_test.go:103: pass sorting [3 2 1]
--- PASS: Test_SelectSort (0.29s)
PASS
ok      command-line-arguments  0.297s

ISorter.go

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

package sorting

type ISorter interface {
    Sort(data []interface{}, comparator CompareFunction) []interface{}
}

type CompareFunction func(a interface{}, b interface{}) CompareResult

type CompareResult int
const LESS CompareResult = -1
const EQUAL CompareResult = 0
const GREATER CompareResult = 1

tSelectSorter.go

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

package select_sort

import "learning/gooop/sorting"

type tSelectSorter struct {
}

func newSelectSorter() sorting.ISorter {
    return &tSelectSorter{}
}

// 选择排序
func (me *tSelectSorter) Sort(data []interface{}, comparator sorting.CompareFunction) []interface{} {
    if data == nil {
        return nil
    }

    size := len(data)
    if size <= 1 {
        return data
    }

    last := size - 1
    for i := 0;i < last;i++ {
        p := me.min(data, i, size, comparator)
        if p != i {
            data[i], data[p] = data[p], data[i]
        }
    }

    return data
}

// 使用线性查找法, 获取最小值的索引
func (me *tSelectSorter) min(data []interface{}, from int, to int, comparator sorting.CompareFunction) int {
    p := from
    v := data[from]

    for i := from + 1;i < to;i++ {
        v1 := data[i]
        if comparator(v1, v) == sorting.LESS {
            p = i
            v = v1
        }
    }

    return p
}

var SelectSorter = newSelectSorter()

(end)

以上是关于手撸golang 基本数据结构与算法 选择排序的主要内容,如果未能解决你的问题,请参考以下文章

手撸golang 基本数据结构与算法 插入排序

手撸golang 基本数据结构与算法 冒泡排序

手撸golang 基本数据结构与算法 归并排序

手撸golang 基本数据结构与算法 堆排序

手撸golang 基本数据结构与算法 二叉查找树

手撸golang 基本数据结构与算法 网页排名/PageRank,随机游走