各类排序算法及go实现

go的sort函数,其中用到了插入排序,希尔排序,快速排序,那么就让我们来了解下常见的排序爸

package main

import "fmt"

// 冒泡排序O(n2)
// 每次遍历,依次对相邻的元素比较,不满足就交换
func bubbleSort(arr []int) {
    length := len(arr)
    for i := 0; i < length; i++ {
        flag := false
        for j := 0; j < length-i-1; j++ {
            if arr[j] > arr[j+1] { // 不写等于,所以是稳定排序算法
                arr[j], arr[j+1] = arr[j+1], arr[j]
                flag = true
            }
        }
        if flag == false {
            return
        }
    }
}

// 插入排序O(n2)
// 初始已排序区间只有一个元素,取未排序区间元素,在已排序区间中找到合适的位置并插入
func insertSort(arr []int) {
    length := len(arr)
    for i := 1; i < length; i++ {
        cur := arr[i]
        j := i - 1
        for ; j >= 0; j-- {
            if cur < arr[j] { // 同理,不写等号,所以是稳定排序
                arr[j+1] = arr[j]
            } else {
                break
            }
        }
        arr[j+1] = cur // 插入数据
    }
}

func insertSortByStep(arr []int, step int) {
    length := len(arr)
    for i := step; i < length; i += step {
        cur := arr[i]
        j := i - step
        for ; j >= 0; j -= step {
            if cur < arr[j] { // 同理,不写等号,所以是稳定排序
                arr[j+step] = arr[j]
            } else {
                break
            }
        }
        arr[j+step] = cur // 插入数据
    }
}

// 希尔排序
/*
希尔排序时间复杂度是 O(n^(1.3-2)),空间复杂度为常数阶 O(1)。希尔排序没有时间复杂度为 O(n(logn)) 的快速排序算法快 ,因此对中等大小规模表现良好,但对规模非常大的数据排序不是最优选择,总之比一般 O(n^2 ) 复杂度的算法快得多。
希尔排序目的为了加快速度改进了插入排序,交换不相邻的元素对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。
在此我们选择增量 gap=length/2,缩小增量以 gap = gap/2 的方式,用序列 {n/2,(n/2)/2...1} 来表示。
举个简单例子,希尔排序一个 12 个元素的数列:[5 9 1 6 8 14 6 49 25 4 6 3],增量 d 的取值依次为:6,3,1:
取 d = 6 对 [5 x x x x x 6 x x x x x] 进行直接插入排序
没有变化。
取 d = 3 对 [5 x x 6 x x 6 x x 4 x x] 进行直接插入排序
排完序后:[4 x x 5 x x 6 x x 6 x x]。
取 d = 1 对 [4 9 1 5 8 14 6 49 25 6 6 3] 进行直接插入排序
因为 d=1 完全就是直接插入排序了。
*/
func shellSort(arr []int) {
    for gap := len(arr) / 2; gap >= 1; gap = gap / 2 {
        insertSortByStep(arr, gap)
    }
}

// 选择排序O(n2)
// 每次从未排序的区间内找到min元素,插入到已排序区间的末尾
func selectSort(arr []int) {
    length := len(arr)
    for i := 0; i < length; i++ {
        minPos := i
        for j := i; j < length; j++ {
            if arr[j] < arr[minPos] {
                minPos = j
            }
        }
        // 可以发现,每次找到的min都要交换位置,所以是不稳定排序
        arr[i], arr[minPos] = arr[minPos], arr[i]
    }
}

// 归并排序O(nlogn) 非稳定算法
// 分治思想,将数据从中间分解为前后两部分,然后对前后两部分分别排序,最后合并在一起
// 递推公式:merge_sort(p,r) = merge(merge_sort(p,q),merge_sort(q+1,r))   q=(r-p)/2 +p
// 终止条件:p>=r,不再分解
func mergeSort(arr []int) []int {
    if len(arr) < 2 {
        return arr
    }
    mid := len(arr) / 2
    left := mergeSort(arr[0:mid])
    right := mergeSort(arr[mid:])
    return merge(left, right)
}

func merge(left, right []int) []int {
    res := []int{}
    i, j := 0, 0
    for ; i < len(left) && j < len(right); {
        if left[i] <= right[j] {
            res = append(res, left[i])
            i++
        } else {
            res = append(res, right[j])
            j++
        }
    }
    //left 先放进结果集,就是稳定排序
    res = append(res, left[i:]...)
    res = append(res, right[j:]...)
    return res
}

// 快速排序O(nlogn) 非稳定算法
// 原理:如果要排序数组p到r的数据,则任择p到r的任意一个数据作为pivot(取最后一个元素),小于pivot的放在左边,大于的放在右边,pivot放在中间
// 经过这部后,p到r的数据被分成了3部分,假设pivot的下表为q,p到q-1的数据都小于pivot,q+1到r的数据都大于pivot,而后一直递归,知道待排序区间大小为1
// 递推公式:quick_sort(p,r) = partition(p,r) + quick_sort(p,q-1) + quick_sort(q+1,r) ,终止条件:p>=r
func quickSort(arr []int) {
    quick(arr, 0, len(arr)-1)
}
func quick(arr []int, p, r int) {
    if p >= r {
        return
    }
    q := partition(arr, p, r)
    quick(arr, p, q-1)
    quick(arr, q+1, r)
}

// 当然也可以申请两个临时数组,一个放小于的数据,一个放大于的数据,最后合并,但是浪费内存,快排就没有了优势
func partition(arr []int, p, r int) (q int) {
    i := p
    pivot := arr[r]
    for ; p < r; p++ {
        if arr[p] < pivot {
            arr[i], arr[p] = arr[p], arr[i]
            i++
        }
    }
    arr[r], arr[i] = arr[i], arr[r]
    return i
}

/*
Q:为什么插入排序比冒泡排序实用性高
A:虽然时间和空间复杂度都一样,但是插入排序每次只是赋值,而冒泡需要交换元素,可以理解为是3次赋值语句

Q:为什么快速排序比归并排序实用性高
A:因为归并排序不是原地排序,需要额外的空间O(n)
*/

func main() {
    arr := []int{1, 4, 2, 7, 4, 5, 3, 9,9283,0,-2}
    //bubbleSort(arr)
    //insertSort(arr)
    //insertSortByStep(arr,1)
    //shellSort(arr)
    //selectSort(arr)
    //res := mergeSort(arr)
    //fmt.Println(res)
    quickSort(arr)
    fmt.Println(arr)
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值