1 选择排序
表现最稳定的排序算法之一 ,因为无论什么数据进去都是O(n2)的时间复杂度,O(1)的空间复杂度 ,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间,不稳定。
举个例子:对于2 9 5 4 8 1 6
step1:在列表中先选择最大元9并与最后一个数字6交换 2 6 5 4 8 1 9;
step2:在剩余列表选择最大元8并与最后一个数字1交换 2 6 5 4 1 8 9;
step3:在剩下列表选择最大元6并与最后一个数字1交换 2 1 5 4 6 8 9;
step4:在剩下列表选择最大元5并与最后一个数字4交换 2 1 4 5 6 8 9;
step5:剩下列表最大元4已经在合适位置不用交换;
step6:在剩下列表选择最大元2并与最后一个数字1交换 1 2 4 5 6 8 9;
最后只剩一个数字,无需再排序,排序完成。
算法时间复杂度分析:第一步迭代需要n-1次比较操作;第二步需要n-2个比较。。。c表示其他常见操作(赋值,每次迭代的比较)
T(n)=n-1+n-2+...+1+c*(n-1)=n^2/2-n/2+c*(n-1)=O(n^2)
func selectionSort(arr []int) {
for i := 0; i < len(arr)-1; i++ {
//最后一个元素无需排序故len-1
maxV := arr[0]
maxVIdx := 0
for j := 0; j < len(arr)-i; j++ {
if maxV < arr[j] {
maxV = arr[j]
maxVIdx = j
}
}
arr[maxVIdx], arr[len(arr)-1-i] = arr[len(arr)-1-i], arr[maxVIdx]
}
}
2 冒泡排序
分len-1轮比较,每一轮两两比较,将最值沉淀。最佳时间复杂度O(N)最差O(N2),空间复杂度O(1),稳定性排序。
例子:2 9 5 4 8 1 6
step1:将2 9交换,因为已经是升序不用再动;将9 5交换成5 9;将9 4 交换成4 9;将9 8交换成8 9;将9 1交换成1 9;将9 6交换成6 9;----最大元素沉底2 5 4 8 1 6 9;
step2: 类似的将8沉底得到2 4 5 1 6 8 9;
step3:类似的将6沉底得到2 4 5 1 6 8 9;
step4:类似的将5沉底得到2 4 1 5 6 8 9;
step5:类似的将4沉底得到2 1 4 5 6 8 9;
step6:类似的将2沉底得到1 2 4 5 6 8 9;
最后完成
算法分析:
在最好情况时第一步就发现已经有序,则无需再操作,比较此时n-1,O(n);
最坏情况:总共n-1次扫描;第k次扫描需要比较n-k次比较
T(n)=1+2+...(n-1)+C*(n-1)=O(n^2);
func bubbleSort(arr []int) {
for loop := 0; loop < len(arr)-1; loop++ {
//重复len-1次
for j := 0; j < len(arr)-1-loop; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
}
3 插入排序
以原始数组第一个元素为有序序列的首个元素,原始数组后面的元素都与有序序列比较,利用有序性找到该元素在有序序列中的位置然后插入,也即当插入第i(i>1)个元素时,前面的data[0],data[1]……data[i-1]已经排好序。这时用data[i]的排序码与data[i-1],data[i-2],……的排序码顺序进行比较,找到插入位置即将data[i]插入,原来位置上的元素向后顺序移动。最佳时间复杂度O(N)最差O(N2),空间复杂度O(1),稳定性排序。
还是例子2 9 5 4 8 1 6
step1:初始时,已排序列表只含列表第一个元素。将9插入:2 9;
step2:已经排序的2,9,将接下来的5插入列表:2 5 9;
step3:已经排序的2 5 9 ,将接下来的4插入列表:2 4 5 9;
step4:已经排序的2 4 5 6,将接下来的8插入列表:2 4 5 8 9;
step5:已经排序的2 4 5 8 9,将接下来的1 插入列表:1 2 4 5 8 9;
step6:已经排序的1 2 4 5 8 9,接下来的6插入列表:1 2 4 5 6 8 9.
step:整个排序完成。
算法性能:第k个迭代,需要将元素插入一个规模k的有序数组,需要k次比较k次数据移动,C是其他操作
T(n)=2*(1+2+...+n-1)+c*(n-2)=O(n^2)
func insertSort(arr []int) {
for i := 1; i < len(arr); i++ {
//已排序列表已经含列表第一个元素故从1开始处理
cur := arr[i]
j := i - 1
for j >= 0 && arr[j] > cur {
arr[j+1] = arr[j]
j--
}
arr[j+1] = cur
}
}
4 希尔排序
该方法是插入的改进,基本思想是:设待排序元素序列有n个元素,首先取一个整数increment(小于n)作为间隔将全部元素分为increment个子序列,所有距离为increment的元素放在同一个子序列中,在每一个子序列中分别实行直接插入排序。然后缩小间隔increment,重复上述子序列划分和排序工作。直到最后取increment=1,将所有元素放在同一个子序列中排序为止。由于开始时,increment的取值较大,每个子序列中的元素较少,排序速度较快,到排序后期increment取值逐渐变小,子序列中元素个数逐渐增多,但由于前面工作的基础,大多数元素已经基本有序,所以排序速度仍然很快。最佳时间复杂度O(NlogN)最差O(NlogN2),空间复杂度O(1),不稳定性排序。
如数组[1,0,2,10,9,70,5,6,3],
我们先选择增量 length/2,如上9/2=4,分为4组,分别是【1,9】,【0,70】,【2,5】,【10,6】加上剩下的一组【3】
然后给每个数组进行排序,得到[1,0,2,6,9,70,5,10,3]
然后再选择增量length/2/2,如上9/2/2=2,分为2组,分别是【1,0,2,6】,【9,70,5,10】,加上剩下的一组【3】
然后给每个数组进行排序,得到[0,1,2,6,5,9,10,70,3]
然后再选择增量length/2/2/2,如上9/2/2/2=1,分为1组,为[0,1,2,6,5,9,10,70,3]
然后排序,得到[0 1 2 3 5 6 9 10 70]
func shellSort(arr []int) {
for deltaDis := len(arr) / 2; deltaDis > 0; deltaDis /= 2 {
//min is 1
for i := deltaDis; i < len(arr); i++ {
cur := arr[i]
j := i - deltaDis
for j >= 0 && arr[j] > cur {
arr[j+deltaDis] = arr[j]
j -= deltaDis
}
arr[j+deltaDis] = cur
}
}
}