排序算法总览
话不多说,直接看一下维基百科的总结图
补充一点排序算法稳定性的定义:
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
- 排序算法是否为稳定的,是由具体算法决定的
基本数据结构
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
展开讲讲
冒泡排序
要点:
- 需要进行n(待排序数据个数)趟比较
- 每一趟都从头开始,依次往后比较相邻数据的大小,根据自己的需求进行交换
- 每一趟都会找出当前无序区的最大或者最小数据,加入到有序区中
使用场景:
- 教学
- 小数据量排序
- 数据基本有序时
func Bubble(data Interface) {
for i := 0; i < data.Len(); i++ {
flag := true
for j := 0; j < data.Len()-i-1; j++ {
if data.Less(j, j+1) {
continue
}
data.Swap(j, j+1)
flag = false
}
if flag {
break
}
}
}
选择排序
要点:
- 需要进行n(待排序数据个数)趟选择
- 每一趟执行过程只比较数据,不交换数据
- 每一趟都会找出当前无序区的最大或者最小数据,将该数据与无序区的第一个元素交换,同时减小无序区间
使用场景:
- 教学
- 小数据量排序
func Selection(data Interface) {
var index int
for i := 0; i < data.Len()-1; i++ {
index = i
for j := i + 1; j < data.Len(); j++ {
if !data.Less(index, j) {
index = j
}
}
data.Swap(i, index)
}
}
插入排序
要点:
- 与打牌时整理牌的思路是一样的
- 手头是有序的牌,桌面是无序的牌
- 每次从桌面拿一张牌,然后跟手中的牌比较,插入合适的位置
使用场景:
- 小数据量排序
- 数据基本有序时
func Insertion(data Interface) {
for i := 1; i < data.Len(); i++ {
for j := i - 1; j >= 0 && data.Less(j+1, j); j-- {
data.Swap(j+1, j)
}
}
}
希尔排序
要点:
- 有“步长”的插入排序
- 正常的插入排序,可以理解成步长为1
- 希尔排序有一个最优的步长序列(1, 5, 19, 41, 109,...)
- 数据基本有序时,插入排序效率高,所以通过步长加速数据的有序化
- 参考维基百科
使用场景:
- 属于插入排序的优化升级版本
- 小数据量
// shell sort (希尔排序,递减增量排序)
// 已知的最好步长序列是由Sedgewick提出的(1, 5, 19, 41, 109,...)
func Shell(data Interface) {
// 步长,只适合小数据量
step := 5
if data.Len() < 5 {
goto END
}
for i := 0; i < step && i < data.Len(); i++ {
for j := i - step; j >= 0 && data.Less(j+step, j); j -= step {
data.Swap(j+step, j)
}
}
// 函数调用会有耗时, 所以直接实现一小插入排序
//Insertion(data)
END:
for i := 1; i < data.Len(); i++ {
for j := i - 1; j >= 0 && data.Less(j+1, j); j-- {
data.Swap(j+1, j)
}
}
}
(自己简单测试一下,十个数据,与插入排序对比,确实有微秒级的提升)
快速排序
要点:
- 需要选取一个基准值,方法很多,通常选取序列的第一个元素
- 双指针,一个指向头,一个指向尾
- 最终目的是将一个序列分成两段,一段比基准值小,另一段比基准值大
- 与基准值相等的元素,根据自己的规则,放在基准值的左边或右边
- 然后对每一段继续进行如上操作
- 分治的思想
- 快速排序的最坏运行情况是 O(n²) (序列基本有序的情况)
- 它的平摊期望时间是 O(nlogn)
使用场景:
- 相对大的数据量
- 在大多数情况下比其他算法性能都要好
// quick sort (快排)
func Quick(data Interface) {
quickSort(data, 0, data.Len())
}
func quickSort(data Interface, s, e int) {
if e-s < 2 {
return
}
i, j := s, e-1
// 基准,这里取第一个元素
pivot := i
// 一趟快排
for i < j {
// 从后向前比较
for ; i < j; j-- {
if !data.Less(j, pivot) {
continue
}
data.Swap(pivot, j)
pivot = j
i++
break
}
// 从前向后比较
for ; i < j; i++ {
if data.Less(i, pivot) {
continue
}
data.Swap(pivot, i)
pivot = i
j--
break
}
}
// 递归
quickSort(data, s, pivot)
quickSort(data, pivot+1, e)
}
(未完待续……)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。