背景
学习Go语言的基础内容后,准备写点内容练练手,用Go实现平时常用的排序算法;也方便之后复习。
package main
import (
"fmt"
"math/rand"
)
//测试Go里的排序
//Go版本
//https://blog.csdn.net/weixin_41922289/article/details/103350301
//java版
//https://blog.csdn.net/weixin_41922289/article/details/100574583#_7
//排序算法的稳定性
//假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,
//即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
//稳定排序,时间复杂度 O(n^2) 空间复杂度 O(1)
//思路:找到切片中的最大值放在最后,再找第二大放在倒数第二位 ...
func bubbleSort(data []int) {
if len(data) <= 1 {
return
}
for i := 0; i < len(data); i++ {
for j := 0; j < len(data)-i-1; j++ {
if data[j] > data[j+1] {
data[j], data[j+1] = data[j+1], data[j]
}
}
}
}
//选择排序
//不稳定排序: 数据项移动最少的排序算法,每次遍历只移动2个元素的位置 时间复杂度:O(n^2) 空间复杂度: O(1)
//思路:和冒泡排序相反,选择排序是先找到最小的放在切片的最前面 ...
func selectSort(data []int) {
for i := 0; i < len(data); i++ {
minIndex := i
for j := i + 1; j < len(data); j++ {
if data[minIndex] > data[j] {
minIndex = j
}
}
data[i], data[minIndex] = data[minIndex], data[i]
}
}
//插入排序
//稳定排序 事件复杂度:O(n^2) 空间复杂度:O(1)
//思路:外侧循环从index = 1开始,保证左侧的元素是有序的,依次向后取元素,对比左侧有序数组,插入合适位置
func insertSort(data []int) {
//假设左侧的元素都是有序的,外侧循环负责遍历获取右侧的排序元素
//内侧的循环负责对比当前拿取的元素应该插入的位置
var j int
for i := 1; i < len(data); i++ {
temp := data[i]
for j = i; j > 0 && data[j-1] > temp; j-- {
data[j] = data[j-1]
}
//不满足则跳出内测循环,此位置就是插入的位置
data[j] = temp
}
}
//希尔排序
//不稳定排序 事件复杂度 O(n) 空间复杂度:O(1)
//思路:基于插入排序,只不过引入了步长的概念,尽量把大的元素放在后面,小的元素放在前边
// 相比较插入排序减少元素移动的次数;还是插入排序的思想
func shellSort(data []int) {
//外侧循环控制步长
var j int
for h := len(data) / 2; h > 0; h /= 2 {
//内测就是一个插入排序
for i := h; i < len(data); i++ {
temp := data[i]
for j = i; j >= h && temp < data[j-h]; j -= h {
data[j] = data[j-h]
}
data[j] = temp
}
}
}
//快速排序
//不稳定排序 时间复杂度:O(nlogn) 空间复杂度:O(1)
//思路:指定一个基准值:base,左右双指针遍历,<=base 放在左侧,>base 放在右侧,指针最后停留的位置
// 就是分割点,重复操作分割点左右两个集合
func quikSort1(data []int) {
if len(data) < 1 {
return
}
l, r := 0, len(data)-1
//基准值
base := data[0]
for l < r {
for l < r && data[r] >= base {
r--
}
if l < r {
data[l] = data[r]
}
for l < r && data[l] < base {
l++
}
if l < r {
data[r] = data[l]
}
}
data[l] = base
quikSort1(data[:l])
quikSort1(data[l+1:])
}
//堆排序 : https://www.jb51.net/article/245744.htm
//大顶堆(大堆):每个节点的值必须大于它的两个子节点 小顶堆(小堆):每个节点的值必须小于它的两个子节点
//完全二叉树:设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
// A(1)
// B(2) C(3)
// D(4) E(5) F(6) G(7)
// H(8) I(9) J(10)
// 堆中最后一个节点的父节点下标: length / 2 -1
// 任何一个下标为n的节点的左右子节点下标为: 左子节点:ln = n * 2 + 1 右子节点: rn = n * 2 + 2
func heapSort(data []int) {
//堆排序,只能确认确认根节点是最大值或者最小值
//调换根节点和最后一个元素的位置,再依次构建i-1个元素的堆... 依次类推
i := len(data)
for i > 1 {
//构建堆
buildHeap(data, i)
//已大堆为例
//swap(&data[0], &data[i-1])
data[0], data[i-1] = data[i-1], data[0]
i--
}
}
func buildHeap(data []int, len int) {
//找到最后一个节点的父节点
parent := len/2 - 1
for parent >= 0 {
heapify(data, parent, len)
parent--
}
fmt.Println(data[:len])
}
func heapify(data []int, parent, len int) {
//判断两个子节点是否比父节点大,如果是则替换
max := parent
lson := parent*2 + 1
rson := parent*2 + 2
if lson < len && data[lson] > data[max] {
max = lson
}
if rson < len && data[rson] > data[max] {
max = rson
}
if parent != max {
swap(&data[max], &data[parent])
heapify(data, max, len)
}
}
//交换位置
func swap(a, b *int) {
*a, *b = *b, *a
}
//归并排序
// 稳定排序 时间复杂度:O(nlogn) 空间复杂度:O(n)
// 思路: 分治法,拆分为左右两部分并分别排好序,再结合成一个新的切片
func mergeSort(data []int) (result []int) {
if len(data) <= 1 {
return data
}
mid := len(data) / 2
left := mergeSort(data[:mid])
right := mergeSort(data[mid:])
return merge(left, right)
}
func merge(left, right []int) (result []int) {
l, r := 0, 0
for l < len(left) && r < len(right) {
if left[l] > right[r] {
result = append(result, right[r])
r++
} else {
result = append(result, left[l])
l++
}
}
result = append(result, left[l:]...)
result = append(result, right[r:]...)
return
}
func main() {
intSlice := make([]int, 0, 30)
for i := 0; i < 30; i++ {
intSlice = append(intSlice, rand.Intn(100))
}
for _, e := range intSlice {
fmt.Printf("%d \t", e)
}
fmt.Printf("\n")
fmt.Println("--------------------------分割线----------------------------")
//冒泡
//bubbleSort(intSlice)
//选择排序
//selectSort(intSlice)
//插入排序
//insertSort(intSlice)
//希尔排序
//shellSort(intSlice)
//快速排序
//quikSort1(intSlice)
//堆排序
//heapSort(intSlice)
//归并排序
mergeSort := mergeSort(intSlice)
for _, s := range mergeSort {
fmt.Printf("%d \t", s)
}
/*for _, s := range intSlice {
fmt.Printf("%d \t", s)
}*/
}