一.冒泡排序
1.算法描述:
(1)比较相邻的元素。如果左边大于右边,就交换他们两个。使得右边比左边大。这样一轮下来,最大的数就会在最右边了。
(2)因为最大的数已经在最右边了,因此对除了最右边的数重复(1)的步骤,这样倒数第二大的数也被选出来了。
(3)持续每次对越来越少的元素重复(2)的步骤,直到没有任何一对数字需要比较。
2.时间复杂度:
O(n^2)
3.代码实现:
package main
import "fmt"
func main() {
data := []int{91, 4, 84, 85, 80, 1, 81, 93, 27,12}
fmt.Println("排序之前:",data)
BubbleSort(data)
}
func BubbleSort (d []int) {
for i := 0; i < len(d)-1 ; i++ {//外循环控制比较次数,10个数比较9次(0~8)
for j := 0; j < len(d)-1-i; j++ {//内循环控制每次,要两两交换的次数,第一次两两交换9次,第二次两两交换8次。。。
if d[j]>d[j+1]{
d[j],d[j+1] = d[j+1],d[j]
}
}
}
fmt.Println("冒泡排序之后:",d)
}
4.执行结果:
二.选择排序
1.算法描述:
(1)假设第0位就是最小值,将该值与剩余的1-n个数进行比较,从1-n中找出真正的最小值,与第0位的数进行交换,这样最小的数就会出现在第0位,这是第一轮
(2)假设第1位是最小值,将该值与剩余的2-n个数进行比较,从2-n中找出真正的最小值,与第1位的数进行交换,这样倒数第二小的数就会出现在第1位,这是第二轮
(3)重复上述步骤,一共需要n-1轮
2.时间复杂度:
O(n^2)
3.代码实现:
package main
import "fmt"
func main() {
data := []int{91, 4, 84, 85, 80, 1, 81, 93, 27,12}
fmt.Println("排序之前:",data)
ChooseSort(data)
}
func ChooseSort (d []int) {
//外循环控制轮次,10个数需要9轮
for i := 0; i < len(d)-1; i++ {//这里i为0~8,共9轮
min := d[i] //假设d[0]就是最小值
minIndex := i //最小值的下标为0
//遍历后面的1~【len(d)-1】与假设的最小值比较
for j := i+1; j < len(d); j++ {
if min > d[j] { //找到真正的最小值,将它的值赋给min,下标赋给minIndex
min = d[j]
minIndex = j
}
}
//最小值找到后,将它与d[0]位置交换
if minIndex != i { //假设的值本身就是最小值,则不用交换,否则就要交换
d[i], d[minIndex] = d[minIndex], d[i]
}
}
fmt.Println("选择排序之后:",d)
}
4.执行结果:
三.插入排序
1.算法描述:
(1)比如有待排序元素[100,88,43,90],现将第一个元素100拿出来当做一个有序表,此时有序表为[100]
(2)然后拿88和100比较,因为88比100小,100向后移动一位,88在100的前面插入,变成[88,100,43,22,1,34,89],此时有序表为[88,100]
(3)再将43和100比较,43小于100,100向后移一位([88,_,100]),再将43和88比较,43比88小,88向后移动一位([_,88,100]),43再向前比较,此时因为前面已经没有数了,所以43插入88前面,此时有序表为[43,88,100]
(4)再将90和(3)中有序表中100比较,90比100小,100向后移动一位([43,88,_,100]),再将90和88比较,因为90比88大,所以90直接在此位置插入,变成[43,88,90,100]
(5)4个数一共经常3次比较,完成排序
2.时间复杂度:
在最坏情况下: 数组完全逆序,插入第2个元素时,前1个元素后移一位;插入第3个元素时,前2个元素都要各后移一位,……,插入第n个元素,前n-1个元素都要各后移一位。因此,最坏情况下的比较次数是 1 + 2 + 3 + ... + (n-1)
,等差数列求和,结果为 (n^2)/2
,所以最坏情况下的复杂度为 O(n^2)
。
在最好情况下:数组已经是有序的,每插入一个元素,只需要和前一个元素比较,因此最好情况下,插入排序的时间复杂度为O(n)
。
3.代码实现:
package main
import "fmt"
func main() {
data := []int{91, 4, 84, 85, 80, 1, 81, 93, 27,12}
fmt.Println("排序之前:",data)
InsertSort(data)
}
//从小到大排列,一开始有序列表[91]
func InsertSort(d []int) {
for i := 1; i < len(d); i++{//10个元素需要9次
insertVal := d[i] //待插入元素
insertIndex := i-1 //待插入元素下标的前一个下标
for insertIndex >= 0 && d[insertIndex] > insertVal {
d[insertIndex + 1] = d[insertIndex] //[91,91]数据后移一位
insertIndex-- //此时insertIndex指向第一个91,再减一等于-1<0了,跳出for循环
}
//插入
if insertIndex + 1 != i {//-1+1=0 != 1
d[insertIndex + 1] = insertVal
}
fmt.Printf("第%d次插入后%v\n",i,d)
}
fmt.Println("插入排序之后:",d)
}
4.执行结果:
四.快速排序
1.算法描述:
(1)假设有待排序元素[3,85,30,51,25,9,78],数组下标为0~6
(2)选择下标为(0+6)/2=3的数作为中枢,这里的中枢值就是51,将比51小的数放左边,比51大的数放右边,完成之后变成[3,9,30,25,51,85,78],此时51左边的数都比51小,51右边的数都比51大
(3)再对[3,9,30,25]和[85,78]重复(2)步骤,变成[3,9,30,25,51,78,85]
(4)再对[30,25]重复(2)步骤,变成[25,30]。此时快速排序完成。最终排序结果[3,9,25,30,51,78,85]
(5)3和4步骤其实一直重复2的步骤,也就是递归运算
2.形象图:
3.时间复杂度:
O(nlgn)
4.代码实现:
package main
import (
"fmt"
)
func main() {
arr := []int{91, 4, 84, 85, 80, 1, 81, 93, 27,12}
fmt.Println("排序之前:",arr)
QuickSort(0, len(data)-1, arr)
fmt.Println("快速排序之后:",arr)
}
//1. left 表示 数组最左边的下标
//2. right 表示数组最右边的下标
//3 arr 表示要排序的数组
func QuickSort(left int, right int, arr []int) {
//left始终指向数组最左边,不会动。right始终指向数组最右边,不会动
//l一开始指向数组最左边,后面会逐渐右移。r一开始指向数组最右边,后面会逐渐左移
l := left
r := right
middle := arr[(left + right) / 2]
//for循环的目标是将比middle小的数放到左边;比middle大的数放到右边
for ; l < r; {
//从middle的左边找到大于等于middle的值
for ; arr[l] < middle; {
l++
}
//从middle的右边边找到小于等于middle的值
for ; arr[r] > middle; {
r--
}
// 1 >= r 表明本次分解任务完成, break
if l >= r {
break
}
//将middle左边大于middle的值 与 middle右边小于middle的值 交换
arr[l],arr[r] = arr[r],arr[l]
//优化
if arr[l]== middle {
r--
}
if arr[r]== middle {
l++
}
}
// 如果 1== r, 再移动下,为了跳出循环,
//同时也是为了下面递归时,找到左边集合数组中最右边的数r,和找到右边集合数组中最左边的数l
if l == r {
l++
r--
}
// 向左递归
if left < r {
QuickSort(left, r, arr)
}
// 向右递归
if right > l {
QuickSort(l, right, arr)
}
}
5.执行结果
五.使用goroutine实现快速排序
package main
import (
"fmt"
"sync"
)
func main() {
data := []int{3, 6, 23, 7, 2, 4, 9, 13,66,61}
fmt.Println(QuickSortOnGoroutine(data))
}
func QuickSortOnGoroutine (data []int) []int {
if len(data) <= 1 {
return data
}
var wg sync.WaitGroup
c := data[0]
var s1, s2 []int
for k, v := range data {
if k == 0 {
continue
}
if c < v {
s2 = append(s2, v)//s2中值总是比data[0]大
} else {
s1 = append(s1, v)//s1中值总是比data[0]小
}
}
wg.Add(2)
go func() {
s1 = QuickSortOnGoroutine(s1)
wg.Done()
}()
go func() {
s2 = QuickSortOnGoroutine(s2)
wg.Done()
}()
wg.Wait()
data = []int{c}
if len(s1) > 0 {
data = append(s1, data...)
}
if len(s2) > 0 {
data = append(data, s2...)
}
return data
}
执行结果:
六.测试对比四个排序算法速度
(1)对10万个随机数使用冒泡排序法,需要时间平均为15~16秒
(2)对10万个随机数使用选择排序法,需要时间平均为5~6秒
(3)对10万个随机数使用插入排序法,需要时间平均为1~2秒
(4)由于快速排序法太快,待排序元素数量太少无法看到时间,所以这里使用1000w个随机数,需要时间平均为1~2秒
(5)使用goroutine实现的快速排序。使用1000w个随机数测试: