排序可以说时最基础的算法之一,排序就是将数据按照某种逻辑重新排列的过程,比如从大到小排序、从小到大排序;排序非常常见比如有购物车物品的排序、历史订单的排序等等;算法我们比较关心的主要有两点:时间复杂度与空间复杂度,排序算法一样;这篇文章只介绍几种基本的排序算法:冒泡排序、插入排序、选择排序;
选择排序
算法逻辑上分为已排序未排序两个区间,从当前列表中选择最小的元素与当前列表第一个元素交换位置,从列表剩余元素中找到最小元素与列表第二个元素交换位置如此重复执行的算法为选择排序算法;
选择排序与数据的初始状态无关,每次查找最小元素值时都会与数组中元素进行交换位置,因此选择排序为非稳定排序算法,在各种情况下算法时间复杂度都为O(n2),空间复杂度为O(n);
func SelectionSort(data [] int) {
n := len(data)
if n <= 1 {
return
}
for i := 0; i < n; i++ {
//最小元素索引
min := i
for j := i + 1; j < n; j++ {
if data[j] < data[min] {
min = j
}
}
//交换元素,交换后未已排序数据
data[i], data[min] = data[min], data[i]
}
}
插入排序
与选择排序类似在逻辑上列表分为已排序和未排序两个区间,初始时已排序区间未为左边第一个元素,插入排序的核心为从右边未排序区间中选择元素插入到左边合适的位置,直到已排序列表长度等于原始列表长度;插入排序算法的关键步骤为元素比较与元素的移动,当从未排序端拿数据插入到已排序端时已排序端可能会存在大量的数据移动;
与选择排序不同的是列表初始状态对插入排序的性能影响很大,当列表初始状态有序度很高时插入排序并不需要大量移动数据,从中可以看出插入排序为原地排序算法、算法是稳定的;
算法最好情况下时间复杂度为:O(n)、平均、最坏复杂度为:O(n2);
func InsertionSort(data [] int) {
n := len(data)
for i := 1; i < n; i++ {
value := data[i]
//查找插入位置
var j int
for j = i - 1; j >= 0; j-- {
if data[j] > value {
//数据后移
data[j+1] = data[j]
} else {
break
}
}
data[j+1] = value
}
}
冒泡排序
每次比较相邻两个元素是否满足,否则交换相邻两个元素,每次冒泡至少一个元素移动到它所属位置,重复N次完成列表排序;
冒泡操作核心为项链元素交换,空间复杂度为O(1),为原地排序算法;算法是稳定的,最好情况时间复杂度为为O(n),平均与最坏情况时间复杂度为:O(n2);
func BubbleSort(data [] int) {
n := len(data)
for i := 0; i < n; i++ {
//是否有数据交换,用于当某轮中数据已排序时退出循环
flag := false
for j := 0; j < n-1-i; j++ {
if data[j] > data[j+1] {
//冒泡
data[j], data[j+1] = data[j+1], data[j]
flag = true
}
}
if !flag {
break
}
}
}
基础排序算法特性
参考资料: 《算法四》