简介
介绍一些常用的排序,包括:快排、冒泡、选择、插入、归并、堆排序,基于go语言的实现。
快排排序
package main
import (
"fmt"
)
func main() {
arr := []int{3, 2, 4, 2, 1, 5, 6, 7, 5, 6, 7, 8, 5, 4, 3, 8, 9, 7, 6, 7}
//快排
quickSort(arr, 0, len(arr)-1)
fmt.Println(arr)
}
/**
经典快排
以数组最后一位数为基准,小于的放左边 等于的放中间 大于的放右边
递归 以上 左边的 和 右边的
不稳定 最坏 O(N^2) 最好和平均 O(NlogN)
随机快排 最坏最好和平均 O(NlogN)
不取最后一位而是随机一位做基准,其他不变 所以它结合概率 期望复杂度是 O(NlogN)
*/
func quickSort(arr []int, L, R int) {
//L不能小于R
if L < R {
//将数组分为左中右三部分,返回等于的区间
partition := partition(arr, L, R)
//递归左部分
quickSort(arr, L, partition[0]-1)
//递归右部分
quickSort(arr, partition[1]+1, R)
}
}
func partition(arr []int, L, R int) []int {
//less下标之前的都是小于待比较数的 默认数组最小下标-1
less := L - 1
//more下标之后的都是大于待比较数的 默认数组最大下标+1
more := R + 1
//待比较数
compare_num := arr[R] //默认为最后一位 随机快排 这个取个随机数即可
//当前比较下标, 比较到哪个位置了
cur := L
for cur < more {
//fmt.Printf("cur:%v,less:%v,more:%v", cur, less, more)
if arr[cur] < compare_num {
//如果当前数小于比较数,则less的后一位与当前元素交换位置并把less加1,当前cur后移一位
arr[less+1], arr[cur] = arr[cur], arr[less+1]
less++
cur++
} else if arr[cur] > compare_num {
//如果当前数大于比较数,则more的前一位与当前元素互换位置并把more减1,此时cur不移动继续比较
arr[more-1], arr[cur] = arr[cur], arr[more-1]
more--
} else {
//相等的话直接往后面移动
cur++
}
//fmt.Println(arr)
}
//返回 相等的区间 也就是less+1和more-1都是与比较数相等的
return []int{less + 1, more - 1}
}
冒泡排序
package main
import (
"fmt"
)
func main() {
//冒泡
arr1 := []int{3, 2, 4, 2, 1, 5, 6, 7, 5, 6, 7, 8, 5, 4, 3, 8, 9, 7, 6, 7}
maopaoSort(arr1)
fmt.Println(arr1)
}
/**
冒泡 和 选择排序
复杂度最高的排序 基本不用 只做演示和教学
复杂度为 选择排序最差最好平均都是O(N^2) 冒泡最好可能是O(N)
冒泡 22比较
选择 1和后面的全部比较
*/
func maopaoSort(arr []int) {
//不解释了, 两两比较
for i := 0; i < len(arr); i++ {
for j := 0; j < len(arr)-i-1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
}
选择排序
package main
import (
"fmt"
)
func main() {
arr2 := []int{3, 2, 4, 2, 1, 5, 6, 7, 5, 6, 7, 8, 5, 4, 3, 8, 9, 7, 6, 7}
xuanzeSort(arr2)
fmt.Println(arr2)
}
func xuanzeSort(arr []int) {
//不解释了, 与后面的一一比较
for i := 0; i < len(arr); i++ {
tmp := arr[i]
for j := i + 1; j < len(arr); j++ {
if tmp > arr[j] {
tmp, arr[j] = arr[j], tmp
}
}
arr[i] = tmp
}
}
插入排序
package main
import (
"fmt"
)
func main() {
//插入
arr3 := []int{3, 2, 4, 2, 1, 5, 6, 7, 5, 6, 7, 8, 5, 4, 3, 8, 9, 7, 6, 7}
charuSort(arr3)
fmt.Println(arr3)
}
/**
插入排序
最坏平均 O(N^2) 最好 O(N)
像斗地主那样, 左边部分都是有序的, 右边拿一个按顺序插入左边
*/
func charuSort(arr []int) {
//从1开始,默认有1个有序
for i := 1; i < len(arr); i++ {
//比较下一个位置,往前面有序数组中插入
for j := i - 1; j >= 0; j-- {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
}
归并排序
package main
import (
"fmt"
)
func main() {
//归并
arr4 := []int{3, 2, 4, 2, 1, 5, 6, 7, 5, 6, 7, 8, 5, 4, 3, 8, 9, 7, 6, 7}
arr4 = guibingSort(arr4)
fmt.Println(arr4)
}
/**
归并排序
最坏最好平均 O(NlogN)
递归
左右 两个数组 依次比较 递归出来的左右两个数组是分别有序
*/
func guibingSort(arr []int) []int {
//数组个数小于2直接返回
if len(arr) < 2 {
return arr
}
index := len(arr) / 2
//归并左边的使其有序
left := guibingSort(arr[0:index])
//归并右边的使其有序
right := guibingSort(arr[index:])
//合并两个有序数组
return merge(left, right)
}
func merge(left []int, right []int) []int {
//临时数组
tmp := make([]int, 0)
//左右两个有序数组都从下标0开始比较
i, j := 0, 0
for i < len(left) && j < len(right) {
if left[i] < right[j] {
//如果左边小于右边,左边加入临时数组,左边下标+1继续比较
tmp = append(tmp, left[i])
i++
} else {
//如果左边大于右边,则相反
tmp = append(tmp, right[j])
j++
}
}
//上面比较完可能有一方还有数组没放进去,则直接放进临时数组,下面两个语句只有一个会执行,可以让左边或右边剩下元素直接放到临时后面即可
tmp = append(tmp, left[i:]...)
tmp = append(tmp, right[j:]...)
//返回合并好的有序数组
return tmp
}
堆排序
package main
import (
"fmt"
)
func main() {
//堆排序
arr5 := []int{3, 2, 4, 2, 1, 5, 6, 7, 5, 6, 7, 8, 5, 4, 3, 8, 9, 7, 6, 7}
heapSort(arr5)
fmt.Println(arr5)
}
/**
堆排序
最坏最好平均 O(NlogN)
每个数组都能转化为堆 堆其实就是一种完全二叉树,分为大根堆和小跟堆
1>先把数组转化为大根堆
2>一次缩小堆长度,将大根堆顶弹出,堆尾换到堆顶进行微调 直至堆长度为0
3 4 5 4 7 9 3
9
5 7
3 4 4 3
*/
func heapSort(arr []int) {
//先把数组转化为大根堆
for i := 0; i < len(arr); i++ {
//(i-1)/2是父节点 如果比这个大 就交换
for i > 0 && arr[i] > arr[(i-1)/2] {
arr[i], arr[(i-1)/2] = arr[(i-1)/2], arr[i]
i = (i - 1) / 2
}
}
//此时arr已经转化为大根堆 开始首尾交换 [9 8 7 8 7 5 4 7 7 6 6 3 5 4 3 2 5 2 6 1]
heapSize := len(arr)
for heapSize > 0 {
//首尾交换
arr[0], arr[heapSize-1] = arr[heapSize-1], arr[0]
//堆长度-1
heapSize--
//调整堆为大根堆 自上而下 子节点是 i\*2+1
//还有子节点
curent := 0
for curent*2+1 < heapSize {
large := 0
//两个子节点比较出最大的 避免越界
if curent*2+2 < heapSize && arr[curent*2+1] < arr[curent*2+2] {
large = curent*2 + 2
} else {
large = curent*2 + 1
}
//比较父子节点,让小数下沉
if arr[large] < arr[curent] {
large = curent
}
//看看比较出的large是不是当前节点,如果是的话说明已经最大就跳出,不是就交换位置继续下沉
if curent != large {
arr[curent], arr[large] = arr[large], arr[curent]
curent = large
} else {
//如果父节点已经最大了就退出
break
}
}
}
}