该包实现了四种基本排序算法:插入排序、归并排序、堆排序和快速排序。 但是这四种排序方法是不公开的,它们只被用于sort包内部使用。所以在对数据集合排序时不必考虑应当选择哪一种排序方法,只要实现了sort.Interface定义的三个方法:获取数据集合长度的Len()方法、比较两个元素大小的Less()方法和交换两个元素位置的Swap()方法,就可以顺利对数据集合进行排序。sort包会根据实际数据自动选择高效的排序算法。 除此之外,为了方便对常用数据类型的操作,sort包提供了对[]int切片、[]float64切片和[]string切片完整支持,主要包括:

对基本数据类型切片的排序支持
基本数据元素查找
判断基本数据类型切片是否已经排好序
对排好序的数据集合逆序

3.1.1 数据集合排序

前面已经提到过,对数据集合(包括自定义数据类型的集合)排序需要实现sort.Interface接口的三个方法,我们看以下该接口的定义:

type Interface interface {
// 获取数据集合元素个数
Len() int
// 如果i索引的数据小于j所以的数据,返回true,不会调用
// 下面的Swap(),即数据升序排序。
Less(i, j int) bool
// 交换i和j索引的两个元素的位置
Swap(i, j int)
}

数据集合实现了这三个方法后,即可调用该包的Sort()方法进行排序。 Sort()方法定义如下:

`func Sort(data Interface)`

Sort()方法惟一的参数就是待排序的数据集合。

该包还提供了一个方法可以判断数据集合是否已经排好顺序,该方法的内部实现依赖于我们自己实现的Len()和Less()方法:

func IsSorted(data Interface) bool {
n := data.Len()
for i := n - 1; i > 0; i-- {
if data.Less(i, i-1) {
return false
}
}
return true
}

下面是一个使用sort包对学生成绩排序的示例:

package main

import (
"fmt"
"sort"
) //学生成绩结构体
type StuScore struct {
//姓名
name string
//成绩
score int
} type StuScores []StuScore //Len()
func (s StuScores) Len() int {
return len(s)
} //Less():成绩将有低到高排序
func (s StuScores) Less(i, j int) bool {
return s[i].score < s[j].score
} //Swap()
func (s StuScores) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
} func main() {
stus := StuScores{
{"alan", 95},
{"hikerell", 91},
{"acmfly", 96},
{"leao", 90}} fmt.Println("Default:")
//原始顺序
for _, v := range stus {
fmt.Println(v.name, ":", v.score)
}
fmt.Println()
//StuScores已经实现了sort.Interface接口
sort.Sort(stus) fmt.Println("Sorted:")
//排好序后的结构
for _, v := range stus {
fmt.Println(v.name, ":", v.score)
} //判断是否已经排好顺序,将会打印true
fmt.Println("IS Sorted?", sort.IsSorted(stus))
}

程序该示例程序的自定义类型StuScores实现了sort.Interface接口,所以可以将其对象作为sort.Sort()和sort.IsSorted()的参数传入。运行结果:

======Default======
alan : 95
hikerell : 91
acmfly : 96
leao : 90 ======Sorted=======
leao : 90
hikerell : 91
alan : 95
acmfly : 96
IS Sorted? true

该示例实现的是升序排序,如果要得到降序排序结果,其实只要修改Less()函数:

//Less():成绩降序排序,只将小于号修改为大于号
func (s StuScores) Less(i, j int) bool {
return s[i].score > s[j].score
}

此外,sort包提供了Reverse()方法,可以允许将数据按Less()定义的排序方式逆序排序,而不必修改Less()代码。方法定义如下:

func Reverse(data Interface) Interface

我们可以看到Reverse()返回的一个sort.Interface接口类型,整个Reverse()的内部实现比较有趣:

    //定义了一个reverse结构类型,嵌入Interface接口
type reverse struct {
Interface
} //reverse结构类型的Less()方法拥有嵌入的Less()方法相反的行为
//Len()和Swap()方法则会保持嵌入类型的方法行为
func (r reverse) Less(i, j int) bool {
return r.Interface.Less(j, i)
} //返回新的实现Interface接口的数据类型
func Reverse(data Interface) Interface {
return &reverse{data}
}

了解内部原理后,可以在学生成绩排序示例中使用Reverse()来实现成绩升序排序:

    sort.Sort(sort.Reverse(stus))
for _, v := range stus {
fmt.Println(v.name, ":", v.score)
}

最后一个方法:Search()

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

官方文档这样描述该方法:

Search()方法回使用“二分查找”算法来搜索某指定切片[0:n],并返回能够使f(i)=true的最 小的i(0<=i<n)值,并且会假定,如果f(i)=true,则f(i+1)=true,即对于切片[0:n],
i之前的切片元素会使f()函数返回false,i及i之后的元素会使f()函数返回true。但是,当 在切片中无法找到时f(i)=true的i时(此时切片元素都不能使f()函数返回true),Search() 方法会返回n。

Search()函数一个常用的使用方式是搜索元素x是否在已经升序排好的切片s中:

    x := 11
s := []int{3, 6, 8, 11, 45} //注意已经升序排序
pos := sort.Search(len(s), func(i int) bool { return s[i] >= x })
if pos < len(s) && s[pos] == x {
fmt.Println(x, "在s中的位置为:", pos)
} else {
fmt.Println("s不包含元素", x)
}

官方文档还给出了一个猜数字的小程序:

func GuessingGame() {
var s string
fmt.Printf("Pick an integer from 0 to 100.\n")
answer := sort.Search(100, func(i int) bool {
fmt.Printf("Is your number <= %d? ", i)
fmt.Scanf("%s", &s)
return s != "" && s[0] == 'y'
})
fmt.Printf("Your number is %d.\n", answer)
}

3.1.2 sort包已经支持的内部数据类型排序

前面已经提到,sort包原生支持[]int、[]float64和[]string三种内建数据类型切片的排序操作,即不必我们自己实现相关的Len()、Less()和Swap()方法。

  1. IntSlice类型及[]int排序

由于[]int切片排序内部实现及使用方法与[]float64和[]string类似,所以只详细描述该部分。

sort包定义了一个IntSlice类型,并且实现了sort.Interface接口:

    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] }
//IntSlice类型定义了Sort()方法,包装了sort.Sort()函数
func (p IntSlice) Sort() { Sort(p) }
//IntSlice类型定义了SearchInts()方法,包装了SearchInts()函数
func (p IntSlice) Search(x int) int { return SearchInts(p, x) }

并且提供的sort.Ints()方法使用了该IntSlice类型:

`func Ints(a []int) { Sort(IntSlice(a)) }`

所以,对[]int切片排序是更常使用sort.Ints(),而不是直接使用IntSlice类型:

    s := []int{5, 2, 6, 3, 1, 4} // 未排序的切片数据
sort.Ints(s)
fmt.Println(s) //将会输出[1 2 3 4 5 6]

如果要使用降序排序,显然要用前面提到的Reverse()方法:

    s := []int{5, 2, 6, 3, 1, 4} // 未排序的切片数据
sort.Sort(sort.Reverse(sort.IntSlice(s)))
fmt.Println(s) //将会输出[6 5 4 3 2 1]

如果要查找整数x在切片a中的位置,相对于前面提到的Search()方法,sort包提供了SearchInts():

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

注意,SearchInts()的使用条件为:切片a已经升序排序

    s := []int{5, 2, 6, 3, 1, 4} // 未排序的切片数据
sort.Ints(s) //排序后的s为[1 2 3 4 5 6]
fmt.Println(sort.SearchInts(s, 3)) //将会输出2
  1. Float64Slice类型及[]float64排序

实现与Ints类似,只看一下其内部实现:

    type Float64Slice []float64

    func (p Float64Slice) Len() int           { return len(p) }
func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] || isNaN(p[i]) && !isNaN(p[j]) }
func (p Float64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p Float64Slice) Sort() { Sort(p) }
func (p Float64Slice) Search(x float64) int { return SearchFloat64s(p, x) }

与Sort()、IsSorted()、Search()相对应的三个方法:

    func Float64s(a []float64)
func Float64sAreSorted(a []float64) bool
func SearchFloat64s(a []float64, x float64) int

要说明一下的是,在上面Float64Slice类型定义的Less方法中,有一个内部函数isNaN()。 isNaN()与math包中IsNaN()实现完全相同,sort包之所以不使用math.IsNaN(),完全是基于包依赖性的考虑,应当看到,sort包的实现不依赖与其他任何包。

  1. StringSlice类型及[]string排序

两个string对象之间的大小比较是基于“字典序”的。

实现与Ints类似,只看一下其内部实现:

    type StringSlice []string

    func (p StringSlice) Len() int           { return len(p) }
func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p StringSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p StringSlice) Sort() { Sort(p) }
func (p StringSlice) Search(x string) int { return SearchStrings(p, x) }

与Sort()、IsSorted()、Search()相对应的三个方法:

    func Strings(a []string)
func StringsAreSorted(a []string) bool
func SearchStrings(a []string, x string) int

golang sort —— 排序算法的更多相关文章

  1. STL源代码分析——STL算法sort排序算法

    前言 因为在前文的<STL算法剖析>中,源代码剖析许多,不方便学习,也不方便以后复习.这里把这些算法进行归类,对他们单独的源代码剖析进行解说.本文介绍的STL算法中的sort排序算法,SG ...

  2. STL中sort排序算法第三个参数_Compare的实现本质

    关于C++ STL vector 中的sort排序算法有三种自定义实现,它们本质上都是返回bool类型,提供给sort函数作为第三个参数. 重载运算符 全局的比较函数 函数对象 我认为从实现方式看,重 ...

  3. 总结: Sort 排序算法

    排序总结 面试经验 硅谷某前沿小Startup面试时,问到的一个题目就是写一个快速排序算法.进而面试官问到了各种算法的算法复杂度,进而又问了Merge Sort 与 QuickSort 的优劣. 对排 ...

  4. Golang之排序算法

    冒泡排序 package main //冒泡排序 import "fmt" func bsort(a []int) { ; i < len(a); i++ { ; j < ...

  5. php和c++自带的排序算法

    PHP的 sort() 排序算法与 C++的 sort() 排序算法均为不稳定的排序算法,也就是说,两个值相同的数经过排序后,两者比较过程中还进行了交换位置,后期开发应主要这个问题

  6. Java 集合中的排序算法浅析

    作者:京东物流 秦彪 1.  引言 排序是一个Java开发者,在日常开发过程中随处可见的开发内容,Java中有丰富的API可以调用使用.在Java语言中,作为集合工具类的排序方法,必定要做到通用.高效 ...

  7. 数据结构和算法(Golang实现)(25)排序算法-快速排序

    快速排序 快速排序是一种分治策略的排序算法,是由英国计算机科学家Tony Hoare发明的, 该算法被发布在1961年的Communications of the ACM 国际计算机学会月刊. 注:A ...

  8. 数据结构和算法(Golang实现)(24)排序算法-优先队列及堆排序

    优先队列及堆排序 堆排序(Heap Sort)由威尔士-加拿大计算机科学家J. W. J. Williams在1964年发明,它利用了二叉堆(A binary heap)的性质实现了排序,并证明了二叉 ...

  9. 经典排序算法 – 插入排序Insertion sort

    经典排序算法 – 插入排序Insertion sort  插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕. 插入排序方法分直接插入排序和折半插入排序两种, ...

  10. 排序算法总结(二)归并排序【Merge Sort】

    一.归并排序原理(Wikipedia) 归并排序本质是分治思想的应用,并且各层分治递归可以同时进行 1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 2.设定两个指针,最初位置 ...

随机推荐

  1. (Nosql)列式存储是什么?

    首先nosql可以被理解为not only sql 泛指非关系型数据库,也就是说不仅仅是sql,所以它既包含了sql的一些东西,但是又和sql不同,并在其的基础上改变或者说扩展了一些东西. 提到nos ...

  2. 运维排查 | Systemd 之服务停止后状态为 failed

    哈喽大家好,我是咸鱼. 我们知道 CentOS 7 之后,Systemd 代替了原来的 SystemV 来管理服务,相比 SystemV ,Systemd 能够很好地解决各个服务间的依赖关系,还能让所 ...

  3. ChatGPT商用网站源码+支持ai绘画(Midjourney)+GPT4.0+GPT3.5key绘画+Prompt角色+实时语音识别输入+后台一键版本更新!

    ChatGPT商用网站源码+支持ai绘画(Midjourney)+GPT4.0+GPT3.5key绘画+Prompt角色+实时语音识别输入+后台一键版本更新! 1.网站系统源码介绍: 程序已支持Cha ...

  4. 搭载KaihongOS的工业平板、机器人、无人机等产品通过3.2版本兼容性测评,持续繁荣OpenHarmony生态

    近日,搭载深圳开鸿数字产业发展有限公司(简称"深开鸿")KaihongOS 软件发行版的工业平板.机器人.无人机等商用产品均通过 OpenAtom OpenHarmony(以下简称 ...

  5. OpenHarmony 技术日直播回顾丨共建新技术,开拓新领域

    4月25日,"共建新技术,开拓新领域"OpenAtom OpenHarmony(以下简称"OpenHarmony")技术日在深圳顺利召开.OpenHarmony ...

  6. C# 继承、多态性、抽象和接口详解:从入门到精通

    C# 继承 在 C# 中,可以将字段和方法从一个类继承到另一个类.我们将"继承概念"分为两类: 派生类(子类) - 从另一个类继承的类 基类(父类) - 被继承的类 要从一个类继承 ...

  7. 编译opencv: Linux编译opencv

    opencv官网:https://opencv.org/releases/ github下载地址:https://github.com/opencv/opencv/releases     mkdir ...

  8. Mac OS 中JDK 环境(jdk 1.8.0_831)安装配置、环境变量配置及卸载操作

    前言: 摊牌了,本来就有点喜新厌旧的我,特意把系统和开发环境都拉到比较高,想试验一下兼容性和某些新特性,探索了一下新大陆,也见识了各种光怪陆离的妖魔鬼怪. 因为要着手云平台项目的重构改版和新系统的架构 ...

  9. 如何在报表中绘制 SVG 统计图

    SVG 作为一种矢量图形,具有任意缩放不失真.可被高质量打印.文件较小.交互性强等优势,正逐渐成为一种主流的图片格式.润乾报表一方面可以生成 SVG 格式的统计图,另一方面也可以在 HTML5 中直接 ...

  10. RestfulApi 学习笔记——查询与过滤还有搜索(五)

    前言 过滤和查询感觉是一个样子,实际上是不同含义.查询是查询一个主体,如果说要查询全部男职工但是名字中带良的,全部男职工 就是主体要查询的对象,然后名字中带良的表示的是过滤. 那么什么是搜索呢?搜索是 ...