生命不止,继续 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
}