Sort 包介绍

  Go 语言标准库 sort 包中实现了几种基本的排序算法:插入排序、快速排序和堆排序,但是在使用 sort 包进行排序时无需具体考虑使用哪种排序方式,因为该方法会根据传入的排序的数据量来进行自动选择合适的排序算法。

func insertionSort(data Interface, a, b int) // 插入排序
func heapSort(data Interface, a, b int) // 堆排序
func quickSort(data Interface, a, b, maxDepth int) // 快速排序

  sort.Interface 接口定义了以下三个方法,只要实现了这三个方法,就可以对数据集合进行排序,sort 包会根据实际数据自动选择高效的排序算法。

// sort.go
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}

Sort 包自带的排序实现

  在 Go 的 sort 包中,给我们实现了三种类型的排序,分别是 Int, string, float64 类型,我们来具体看一下 Int 类型的实现:

// sort.go
// IntSlice attaches the methods of Interface to []int, sorting in increasing order.
type IntSlice []int func (p IntSlice) Len() int { return len(p) }
func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p IntSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Sort is a convenience method.
func (p IntSlice) Sort() { Sort(p) }

  可以看到 Go 已经给 int 数组实现了这三种方法,其它两种类型基本相同,所以我们在对上述三种类型的数据进行排序的时候,可以不必自己实现上述接口,直接调用得到排序结果。

Sort 包自定义排序方式

  通过以上描述,我们知道只有上述三种类型是 Go 帮我们实现好的,那如果我们要对结构体进行排序,该如何实现呢?同理,我们只需要给自己定义的待排序的结构体类型实现上述接口就可以了。我们来看一下具体示例:

func main() {
a := personSlice{
{
Name: "AAA",
Age: ,
},
{
Name: "BBB",
Age: ,
},
{
Name: "CCC",
Age: ,
},
{
Name: "DDD",
Age: ,
},
{
Name: "EEE",
Age: ,
},
}
sort.Sort(a)
fmt.Println("Sort: ", a)
sort.Stable(a) // 稳定排序
fmt.Println("Stable: ", a)
}

自定义排序示例

Sort 包如何选择最合适的排序方式

  我们上面说用 sort 进行排序不用关心采用哪种排序方式,但是 sort 又是根据什么来选择排序方式的呢?在 sort 包自带的排序实现一节中,我们看到,上面的类型都是通过 sort.Sort() ,进一步调用 Sort(p) 来进行排序的,那我们来看一下 Sort(p) 所采用的排序策略:

// sort.go
// Sort sorts data.
// It makes one call to data.Len to determine n, and O(n*log(n)) calls to
// data.Less and data.Swap. The sort is not guaranteed to be stable.
func Sort(data Interface) {
n := data.Len()
quickSort(data, , n, maxDepth(n))
}

  可以看到 Sort 函数的参数为接口类型,这说明可以传入任何类型的参数 ;然后调用 quickSort 函数进行排序,其中参数 maxDepth(n) 为什么呢?我们看一下:

// sort.go
// maxDepth returns a threshold at which quicksort should switch
// to heapsort. It returns 2*ceil(lg(n+1)).
func maxDepth(n int) int {
var depth int
for i := n; i > ; i >>= {
depth++
}
return depth *
}

  注释告诉我们,这个函数返回一个阈值,用来区分是用快排还是用堆排序。那我们回去看一下 quickSort 是怎么根据这个阈值进行区分的:

// sort.go
func quickSort(data Interface, a, b, maxDepth int) {
for b-a > { // Use ShellSort for slices <= 12 elements
if maxDepth == {
heapSort(data, a, b)
return
}
maxDepth--
mlo, mhi := doPivot(data, a, b)
// Avoiding recursion on the larger subproblem guarantees
// a stack depth of at most lg(b-a).
if mlo-a < b-mhi {
quickSort(data, a, mlo, maxDepth)
a = mhi // i.e., quickSort(data, mhi, b)
} else {
quickSort(data, mhi, b, maxDepth)
b = mlo // i.e., quickSort(data, a, mlo)
}
}
if b-a > {
// Do ShellSort pass with gap 6
// It could be written in this simplified form cause b-a <= 12
for i := a + ; i < b; i++ {
if data.Less(i, i-) {
data.Swap(i, i-)
}
}
insertionSort(data, a, b)
}
}

  从上面的代码逻辑中可以看到,当元素个数(b-a) 大于 12 时,采用快排(算法中选取选取枢纽值进行了优化),当 maxDepth == 0 时采用堆排序(此处不是很理解);

当 元素个数(b-a) 大于 1 并且小于等于 12 时,首先进行一轮 step == 6 的希尔排序,然后进行插入排序。

Sort 包常用方法

func main() {
a := []int{1, 3, 5, 7, 2}
b := sort.IntSlice{2, 4, 6, 8}
fmt.Println(b)
//=====int=====
c := sort.IntSlice{2, 1, 4, 3, 7}
fmt.Println(c)
sort.Ints(c)
sort.IntsAreSorted(c)
sort.IsSorted(c)
sort.SearchInts(a, 5)
//=====string====
d := sort.StringSlice{"aaa", "bbb", "ccc"}
fmt.Println(d)
sort.Strings(d)
sort.StringsAreSorted(d)
sort.IsSorted(d)
sort.SearchStrings(d, "bbb")
//=====Float64=====
e := sort.Float64Slice{2.1, 2.6, 7.8, 0.5}
fmt.Println(e)
sort.Float64s(e)
sort.Float64sAreSorted(e)
sort.IsSorted(e)
sort.SearchFloat64s(e, 7.8)
//====common function===
/* search(n, func) 使用二分法进行查找指定切片[0:n],返回能使 f(i)=true 的最小的 i(0<=i<n) 的值
并且会假定,如果 f(i)=true, 则 f(i+1)=true
当在切片中无法找到 f(i)=true 的 i 时, 该方法返回 n,而不是返回 -1
notice: 上述的三种 search 查找方法中,其对应的 slice 必须按照升序进行排序,否则会出现奇怪的结果
*/
f := []int{1, 2, 3}
ff := sort.Search(len(f), func(i int) bool {
return f[i] == 8
}) // ff=len(f)==3
fmt.Println(ff)
/* Sort(data Interface) 会对 data 进行排序,为不稳定排序,可以根据不同形式决定
使用不同的排序方式(插入排序,堆排序,快排)
*/
/* Reverse(data Interface) Interface 实现对 data 的逆序排列,结果返回一个 Interface
需要再次调用 sort.Sort(data Interface) 对逆序的结果进行输出
*/
g := sort.IntSlice{3, 2, 4}
sort.Sort(sort.Reverse(g))
fmt.Println(g) // 4, 3, 2
}

Golang---sort包的更多相关文章

  1. golang sort包使用

    https://studygolang.com/static/pkgdoc/pkg/sort.htm#StringSlice.Search package main import ( "fm ...

  2. golang sort包 排序

    []float64: ls := sort.Float64Slice{ 1.1, 4.4, 5.5, 3.3, 2.2, } fmt.Println(ls) //[1.1 4.4 5.5 3.3 2. ...

  3. Golang学习 - sort 包

    ------------------------------------------------------------ // 满足 Interface 接口的类型可以被本包的函数进行排序. type ...

  4. Golang fmt包使用小技巧

    h1 { margin-top: 0.6cm; margin-bottom: 0.58cm; direction: ltr; color: #000000; line-height: 200%; te ...

  5. Golang Vendor 包机制 及 注意事项

    现在的 Go 版本是 1.8,早在 1.5 时期,就有了 Vendor 包机制,详情可查看博文:“理解 Go 1.5 vendor”. 遇到的问题 个人在使用 Glide 管理 Vendor 包时(附 ...

  6. Golang Vendor 包管理工具 glide 使用教程

    Glide 是 Golang 的 Vendor 包管理器,方便你管理 vendor 和 verdor 包.类似 Java 的 Maven,PHP 的 Composer. Github:https:// ...

  7. golang reflect包使用解析

    golang reflect包使用解析 参考 Go反射编码 2个重要的类型 Type Value 其中Type是interface类型,Value是struct类型,意识到这一点很重要 Type和Va ...

  8. Golang测试包

    Golang测试包 golang自带了测试包(testing),直接可以进行单元测试.性能分析.输出结果验证等.简单看着官方文档试了试,总结一下: 目录结构和命令 使用golang的测试包,需要遵循简 ...

  9. Golang : cobra 包解析

    笔者在<Golang : cobra 包简介>一文中简要的介绍了 cobra 包及其基本的用法,本文我们从代码的角度来了解下 cobra 的核心逻辑. Command 结构体 Comman ...

  10. Golang : pflag 包简介

    笔者在前文中介绍了 Golang 标准库中 flag 包的用法,事实上有一个第三方的命令行参数解析包 pflag 比 flag 包使用的更为广泛.pflag 包的设计目的就是替代标准库中的 flag ...

随机推荐

  1. 白手起家Django项目发布下篇_Django项目nginx部署

    上一篇完成了python的安装,接下来安装python的依赖包和项目的依赖包 1.  python-devel 命令:yum -y install python-devel 安装Django1.8.2 ...

  2. Django 利用第三方平台实现用户注册02

    前言: 上篇博客我们已经对设置了图形验证码,短信验证码对用户信息进行了一些简单的验证,本篇博客我们会将上篇的一些验证方法进行结合,来进一步完成我们的注册工作 1. 创建视图类 在user中的view创 ...

  3. BusyBox 添加 自定义命令\小程序 (applet)

    背景 在做嵌入式开的时候,busybox无疑是非常好用的命令集,所以很多时候都喜欢把busybox移植到我们的系统里面. 说明 添加一个很简单的命令--hello_busybox,输出"He ...

  4. Linux:Shell-Bash基本功能

    1.历史命令 history [选项] [历史命令保存文件] 选项:-c  清空历史命令 -w 把缓存中的历史命令写入历史命令保存文件 ~/.bash_history 历史命令默认保存1000条,可以 ...

  5. 在GNOME开发人员的努力下,Pango 1.44即将问世

    早在5月份,Red Hat的Matthias Clasen共同制定了计划,在近年来相当陈旧的情况下,对Pango布局引擎库进行了一些改进. 这项工作将随着Pango1.44版本的发布而实现,看起来它很 ...

  6. 文本处理三剑客与shell正则表达式

    文本处理三剑客 提到对于文本的处理上,除了vim这个强大的编辑器之外,还有使用命令的形式去处理你要处理的文本,而不需要手动打开文本再去编辑.这样做的好处是能够以shell命令的形式将编辑和处理文本的工 ...

  7. [Codeforces #608 div2]1272B Blocks

    Description There are nnn blocks arranged in a row and numbered from left to right, starting from on ...

  8. Android Studio相关

    1.下载安装 Android Studio 2.打开已有或是新建工程,gradle编译时候会报错(被墙),可以切换阿里的源 修改build.gradle 的配置: buildscript { repo ...

  9. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 字体图标(Glyphicons):glyphicon glyphicon-user

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...

  10. 我为NET狂官方面试题-数据库篇答案(转)

    题目:http://www.cnblogs.com/dunitian/p/6028838.html 汇总:http://www.cnblogs.com/dunitian/p/5977425.html ...