生命不止,继续 go go go !!!

排序,对于每种编程语言都是要面对的。这里跟大家一起分享golang实现一些排序算法,并且说明如何生成随机数。
当然,golang为我们提供了sort包,也提供了math/rand包,这就大大方便了我们。

还要说明一下,这里不会详细介绍各种排序算法的原理,如需探索自行Google。

sort package

Package sort provides primitives for sorting slices and user-defined collections.
golang中也实现了排序算法的包sort包.

type Interface

type Interface interface {

    Len() int    // Len 为集合内元素的总数

    Less(i, j int) bool //如果index为i的元素小于index为j的元素,则返回true,否则返回false

    Swap(i, j int)  // Swap 交换索引为 i 和 j 的元素
}

sort相关的一些方法:

func Float64s(a []float64)
将类型为float64的slice a以升序方式进行排序

func Float64sAreSorted(a []float64) bool
判定是否已经进行排序func Ints(a []int)

func Ints(a []int)
以升序排列 int 切片。

func IntsAreSorted(a []int) bool
判断 int 切片是否已经按升序排列。

func IsSorted(data Interface) bool
判断数据是否已经排序。包括各种可sort的数据类型的判断.

func Strings(a []string)
以升序排列 string 切片。

func StringsAreSorted(a []string) bool
判断 string 切片是否已经按升序排列。

func Sort(data Interface)
对 data 进行排序(不保证相等元素的相对顺序不变)
data 默认为升序,执行 Reverse 后为降序。

func Stable(data Interface)
对 data 进行排序(保证相等元素的相对顺序不变)
data 默认为升序,执行 Reverse 后为降序。

func Reverse(data Interface) Interface
将 data 的排序动作更改为降序,Reverse 并不改变元素顺序,只改变排序行为。
更改操作不可逆,更改后的对象不可以再次 Reverse。

应用:

package main

import (
    "fmt"
    "sort"
)

type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%s: %d", p.Name, p.Age)
}

// ByAge implements sort.Interface for []Person based on
// the Age field.
type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }

func main() {
    people := []Person{
        {"Bob", 31},
        {"John", 42},
        {"Michael", 17},
        {"Jenny", 26},
    }

    fmt.Println(people)
    sort.Sort(ByAge(people))
    fmt.Println(people)

}

search相关的方法

func Search(n int, f func(int) bool) int

search使用二分法进行查找

Search 常用于在一个已排序的,可索引的数据结构中寻找索引为 i 的值 x,例如数组或切片。这种情况下,实参 f,一般是一个闭包,会捕获所要搜索的值,以及索引并排序该数据结构的方式。

func SearchFloat64s(a []float64, x float64) int

SearchFloat64s 在float64s切片中搜索x并返回索引如Search函数所述. 返回可以插入x值的索引位置,如果x不存在,返回数组a的长度切片必须以升序排列

func SearchInts(a []int, x int) int

SearchInts 在ints切片中搜索x并返回索引如Search函数所述. 返回可以插入x值的索引位置,如果x不存在,返回数组a的长度切片必须以升序排列

func SearchStrings(a []string, x string) int

SearchFloat64s 在strings切片中搜索x并返回索引如Search函数所述. 返回可以插入x值的索引位置,如果x不存在,返回数组a的长度切片必须以升序排列

应用:

func main() {
    a := sort.StringSlice{"hello", "world", "golang", "sort", "nice"}
    a.Sort() // 二分法必须先排序
    // 获取首字母大于 n 的元素中最小的
    i := sort.Search(len(a), func(i int) bool {
        return len(a[i]) > 0 && a[i][0] > 'n'
    })
    // 显示找到的元素
    fmt.Println(a[i]) // sort
}

math/rand package

Package rand implements pseudo-random number generators.
这里的方法相对简单,就不详细介绍了,可以去看官方文档:

https://golang.org/pkg/math/rand/

下面介绍一下应用:

生成不重复的随机数

func generateRandomNumber(start int, end int, count int) []int {
    if end < start || (end-start) < count {
        return nil
    }

    nums := make([]int, 0)
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    for len(nums) < count {
        num := r.Intn((end - start)) + start

        exist := false
        for _, v := range nums {
            if v == num {
                exist = true
                break
            }
        }

        if !exist {
            nums = append(nums, num)
        }
    }

    return nums
}

各种排序算法时间及比较

冒泡排序

冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

func bubbleSort(items []int) {
    var (
        n       = len(items)
        swapped = true
    )
    for swapped {
        swapped = false
        for i := 0; i < n-1; i++ {
            if items[i] > items[i+1] {
                items[i+1], items[i] = items[i], items[i+1]
                swapped = true
            }
        }
        n = n - 1
    }
}

使用sort包:

func bubbleSortUsingSortPackage(data sort.Interface) {
    r := data.Len() - 1
    for i := 0; i < r; i++ {
        for j := r; j > i; j-- {
            if data.Less(j, j-1) {
                data.Swap(j, j-1)
            }
        }
    }
}

插入排序

插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

func insertionSort(items []int) {
    var n = len(items)
    for i := 1; i < n; i++ {
        j := i
        for j > 0 {
            if items[j-1] > items[j] {
                items[j-1], items[j] = items[j], items[j-1]
            }
            j = j - 1
        }
    }
}

使用sort包:

func insertionSortUsingSortPackage(data sort.Interface) {
    r := data.Len() - 1
    for i := 1; i <= r; i++ {
        for j := i; j > 0 && data.Less(j, j-1); j-- {
            data.Swap(j, j-1)
        }
    }
}

选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。

func selectionSort(items []int) {
    var n = len(items)
    for i := 0; i < n; i++ {
        var minIdx = i
        for j := i; j < n; j++ {
            if items[j] < items[minIdx] {
                minIdx = j
            }
        }
        items[i], items[minIdx] = items[minIdx], items[i]
    }
}

使用sort包:

func selectionSortUsingSortPackage(data sort.Interface) {
    r := data.Len() - 1
    for i := 0; i < r; i++ {
        min := i
        for j := i + 1; j <= r; j++ {
            if data.Less(j, min) {
                min = j
            }
        }
        data.Swap(i, min)
    }
}

快速排序

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。

func QuickSort(src []int, first, last int) {
    flag := first
    left := first
    right := last

    if first >= last {
        return
    }

    for first < last {
        for first < last {
            if src[last] >= src[flag] {
                last -= 1
                continue
            } else {
                tmp := src[last]
                src[last] = src[flag]
                src[flag] = tmp
                flag = last
                break
            }
        }

        for first < last {
            if src[first] <= src[flag] {
                first += 1
                continue
            } else {
                tmp := src[first]
                src[first] = src[flag]
                src[flag] = tmp
                flag = first
                break
            }
        }
    }

    QuickSort(src, left, flag-1)
    QuickSort(src, flag+1, right)
}

希尔排序

希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳定的改进版本。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

1、插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率

2、但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位>

func shellshort(items []int) {
    var (
        n    = len(items)
        gaps = []int{1}
        k    = 1
    )

    for {
        gap := pow(2, k) + 1
        if gap > n-1 {
            break
        }
        gaps = append([]int{gap}, gaps...)
        k++
    }

    for _, gap := range gaps {
        for i := gap; i < n; i += gap {
            j := i
            for j > 0 {
                if items[j-gap] > items[j] {
                    items[j-gap], items[j] = items[j], items[j-gap]
                }
                j = j - gap
            }
        }
    }
}

func pow(a, b int) int {
    p := 1
    for b > 0 {
        if b&1 != 0 {
            p *= a
        }
        b >>= 1
        a *= a
    }
    return p
}

梳排序

func combsort(items []int) {
    var (
        n = len(items)
        gap = len(items)
        shrink = 1.3
        swapped = true
    )

    for swapped {
        swapped = false
            gap = int(float64(gap) / shrink)
            if gap < 1 {
            gap = 1
        }
        for i := 0; i+gap < n; i++ {
            if items[i] > items[i+gap] {
                items[i+gap], items[i] = items[i], items[i+gap]
                swapped = true
            }
        }
    }
}

归并排序

归并排序(Merge sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用

func mergeSort(items []int) []int {
    var n = len(items)

    if n == 1 {
        return items
    }

    middle := int(n / 2)
    var (
        left  = make([]int, middle)
        right = make([]int, n-middle)
    )
    for i := 0; i < n; i++ {
        if i < middle {
            left[i] = items[i]
        } else {
            right[i-middle] = items[i]
        }
    }

    return merge(mergeSort(left), mergeSort(right))
}

func merge(left, right []int) (result []int) {
    result = make([]int, len(left)+len(right))

    i := 0
    for len(left) > 0 && len(right) > 0 {
        if left[0] < right[0] {
            result[i] = left[0]
            left = left[1:]
        } else {
            result[i] = right[0]
            right = right[1:]
        }
        i++
    }

    // Either left or right may have elements left; consume them.
    // (Only one of the following loops will actually be entered.)
    for j := 0; j < len(left); j++ {
        result[i] = left[j]
        i++
    }
    for j := 0; j < len(right); j++ {
        result[i] = right[j]
        i++
    }

    return
}

完整代码

package main

import (
    "fmt"
    "math/rand"
    "sort"
    "time"
)

func main() {
    rand_numbs := generateRandomNumber(0, 30000, 10000)
    items_bubble := rand_numbs
    items_bubble2 := rand_numbs

    items_selection := rand_numbs
    items_selection2 := rand_numbs

    items_insertion := rand_numbs
    items_insertion2 := rand_numbs

    items_quick := rand_numbs
    items_quick2 := rand_numbs

    items_shell := rand_numbs

    items_comb := rand_numbs

    items_merge := rand_numbs

    start := time.Now()
    bubbleSort(items_bubble)
    elapsed := time.Since(start)
    fmt.Printf("Cost time bubbleSort %v\n", elapsed)

    start = time.Now()
    bubbleSortUsingSortPackage(sort.IntSlice(items_bubble2))
    end := time.Now()
    fmt.Printf("Cost time bubbleSortUsingSortPackage %v\n", end.Sub(start))

    start = time.Now()
    selectionSort(items_selection)
    end = time.Now()
    fmt.Printf("Cost time selectionSort %v\n", end.Sub(start))

    start = time.Now()
    selectionSortUsingSortPackage(sort.IntSlice(items_selection2))
    end = time.Now()
    fmt.Printf("Cost time selectionSortUsingSortPackage %v\n", end.Sub(start))

    start = time.Now()
    insertionSort(items_insertion)
    end = time.Now()
    fmt.Printf("Cost time insertionSort %v\n", end.Sub(start))

    start = time.Now()
    insertionSortUsingSortPackage(sort.IntSlice(items_insertion2))
    end = time.Now()
    fmt.Printf("Cost time insertionSortUsingSortPackage %v\n", end.Sub(start))

    start = time.Now()
    QuickSort(items_quick, 0, len(items_quick)-1)
    end = time.Now()
    fmt.Printf("Cost time QuickSort %v\n", end.Sub(start))

    start = time.Now()
    sort.Ints(items_quick2)
    end = time.Now()
    fmt.Printf("Cost time sort.Ints %v\n", end.Sub(start))

    start = time.Now()
    shellshort(sort.IntSlice(items_shell))
    end = time.Now()
    fmt.Printf("Cost time shellshort %v\n", end.Sub(start))

    start = time.Now()
    combsort(sort.IntSlice(items_comb))
    end = time.Now()
    fmt.Printf("Cost time combsort %v\n", end.Sub(start))

    start = time.Now()
    mergeSort(sort.IntSlice(items_merge))
    end = time.Now()
    fmt.Printf("Cost time mergeSort %v\n", end.Sub(start))

}

func bubbleSort(items []int) {
    var (
        n       = len(items)
        swapped = true
    )
    for swapped {
        swapped = false
        for i := 0; i < n-1; i++ {
            if items[i] > items[i+1] {
                items[i+1], items[i] = items[i], items[i+1]
                swapped = true
            }
        }
        n = n - 1
    }
}

func bubbleSortUsingSortPackage(data sort.Interface) {
    r := data.Len() - 1
    for i := 0; i < r; i++ {
        for j := r; j > i; j-- {
            if data.Less(j, j-1) {
                data.Swap(j, j-1)
            }
        }
    }
}

func selectionSort(items []int) {
    var n = len(items)
    for i := 0; i < n; i++ {
        var minIdx = i
        for j := i; j < n; j++ {
            if items[j] < items[minIdx] {
                minIdx = j
            }
        }
        items[i], items[minIdx] = items[minIdx], items[i]
    }
}

func selectionSortUsingSortPackage(data sort.Interface) {
    r := data.Len() - 1
    for i := 0; i < r; i++ {
        min := i
        for j := i + 1; j <= r; j++ {
            if data.Less(j, min) {
                min = j
            }
        }
        data.Swap(i, min)
    }
}

func insertionSort(items []int) {
    var n = len(items)
    for i := 1; i < n; i++ {
        j := i
        for j > 0 {
            if items[j-1] > items[j] {
                items[j-1], items[j] = items[j], items[j-1]
            }
            j = j - 1
        }
    }
}

func insertionSortUsingSortPackage(data sort.Interface) {
    r := data.Len() - 1
    for i := 1; i <= r; i++ {
        for j := i; j > 0 && data.Less(j, j-1); j-- {
            data.Swap(j, j-1)
        }
    }
}

func QuickSort(src []int, first, last int) {
    flag := first
    left := first
    right := last

    if first >= last {
        return
    }

    for first < last {
        for first < last {
            if src[last] >= src[flag] {
                last -= 1
                continue
            } else {
                tmp := src[last]
                src[last] = src[flag]
                src[flag] = tmp
                flag = last
                break
            }
        }

        for first < last {
            if src[first] <= src[flag] {
                first += 1
                continue
            } else {
                tmp := src[first]
                src[first] = src[flag]
                src[flag] = tmp
                flag = first
                break
            }
        }
    }

    QuickSort(src, left, flag-1)
    QuickSort(src, flag+1, right)
}

func shellshort(items []int) {
    var (
        n    = len(items)
        gaps = []int{1}
        k    = 1
    )

    for {
        gap := pow(2, k) + 1
        if gap > n-1 {
            break
        }
        gaps = append([]int{gap}, gaps...)
        k++
    }

    for _, gap := range gaps {
        for i := gap; i < n; i += gap {
            j := i
            for j > 0 {
                if items[j-gap] > items[j] {
                    items[j-gap], items[j] = items[j], items[j-gap]
                }
                j = j - gap
            }
        }
    }
}

func pow(a, b int) int {
    p := 1
    for b > 0 {
        if b&1 != 0 {
            p *= a
        }
        b >>= 1
        a *= a
    }
    return p
}

func combsort(items []int) {
    var (
        n       = len(items)
        gap     = len(items)
        shrink  = 1.3
        swapped = true
    )

    for swapped {
        swapped = false
        gap = int(float64(gap) / shrink)
        if gap < 1 {
            gap = 1
        }
        for i := 0; i+gap < n; i++ {
            if items[i] > items[i+gap] {
                items[i+gap], items[i] = items[i], items[i+gap]
                swapped = true
            }
        }
    }
}

func mergeSort(items []int) []int {
    var n = len(items)

    if n == 1 {
        return items
    }

    middle := int(n / 2)
    var (
        left  = make([]int, middle)
        right = make([]int, n-middle)
    )
    for i := 0; i < n; i++ {
        if i < middle {
            left[i] = items[i]
        } else {
            right[i-middle] = items[i]
        }
    }

    return merge(mergeSort(left), mergeSort(right))
}

func merge(left, right []int) (result []int) {
    result = make([]int, len(left)+len(right))

    i := 0
    for len(left) > 0 && len(right) > 0 {
        if left[0] < right[0] {
            result[i] = left[0]
            left = left[1:]
        } else {
            result[i] = right[0]
            right = right[1:]
        }
        i++
    }

    // Either left or right may have elements left; consume them.
    // (Only one of the following loops will actually be entered.)
    for j := 0; j < len(left); j++ {
        result[i] = left[j]
        i++
    }
    for j := 0; j < len(right); j++ {
        result[i] = right[j]
        i++
    }

    return
}

func generateRandomNumber(start int, end int, count int) []int {
    if end < start || (end-start) < count {
        return nil
    }

    nums := make([]int, 0)

    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    for len(nums) < count {
        num := r.Intn((end - start)) + start

        exist := false
        for _, v := range nums {
            if v == num {
                exist = true
                break
            }
        }

        if !exist {
            nums = append(nums, num)
        }
    }

    return nums
}

转自:https://m.toutiao.com/is/eefpeLd/