源码仓库
github仓库
https://github.com/jackzcai/golang-interview
归并排序
概述
归并排序也是一种基于分治法的排序算法。为了排序长度为n的数组,就先排序两个长度为n/2的子数组,然后合并这两个排序的子数组。
迭代
核心思想在于迭代的时候第一层循环定义seg的长度,每次循环seg乘二。内层循环定义排序的起始位置和终止位置,显然和seg的长度有关。排序的方式是设置两个指针放在src数组的start位置和mid位置,将两个指针中较小的数填入dst数组中对应的位置,然后对应指针右移,迭代执行直到两个指针都到达mid或者end的位置。注意每次迭代的时候dst数组的指针都要右移。最后要交换src和dst数组的位置,因为此时从以dst数组的内容开始排序。
// 迭代实现归并排序
func mergeSortIteration(nums []int) {
lenth := len(nums)
src := nums //src源数组 dst目标数组 操作后要交换两个数组
dst := make([]int, lenth)
for seg := 1; seg < lenth; seg = seg << 1 {
for start := 0; start < lenth; start += seg * 2 {
mid := min(start+seg, lenth)
end := min(start+seg*2, lenth)
i, j, k := start, mid, start //注意start,end左闭右开
for i < mid || j < end {
if j == end || (i < mid && (src[i] < src[j])) {
dst[k] = src[i]
i++
} else {
dst[k] = src[j]
j++
}
k++
}
}
src, dst = dst, src
}
nums = src
}
递归
核心思想在于先排序两个长度为n/2的数组,然后合并这两个数组。注意先定义dst数组为src数组的copy,递归目的在于将src数组的start到end之间的数字排序好放到dst数组的start到end之间,所以内部递归的时候dst和src的位置还是要调换,最后是从dst数组中取得结果。
// 递归实现归并排序
func mergeSortRecursion(src, dst []int, start, end int) { //注意start,end左闭右开
if start+1 >= end {
return
}
mid := (start + end) / 2
mergeSortRecursion(dst, src, start, mid) //注意交换src,dst 这一步的目的是为了将src的start, mid变成顺序的,所以要交换dist和mid
mergeSortRecursion(dst, src, mid, end)
i, j, k := start, mid, start
for i < mid || j < end {
if j == end || (i < mid && (src[i] < src[j])) {
dst[k] = src[i]
i++
} else {
dst[k] = src[j]
j++
}
k++
}
}
时间复杂度
由于长度为n的数组每次都被分为一半,所以不管输入什么样的数组,归并排序的时间复杂度都是Onlogn。归并排序需要创建一个长度为n的辅助数组,如果用递归实现,递归的调用栈需要Ologn的空间。因此归并排序的时间复杂度是On。
同时,这种分治的思想可以用来解决很多其他问题。比如合并排序链表,也是用到了merge的思想。
冒泡排序
概述
冒泡排序是一种简单的排序算法,它也是一种稳定排序算法。其实现原理是重复扫描待排序序列,并比较每一对相邻的元素,当该对元素顺序不正确时进行交换。一直重复这个过程,直到没有任何两个相邻元素可以交换,就表明完成了排序。
优化
在循环的过程中可能已经完成排序,后续就可能发生很多无意义的比较和交换。
所以第一次优化:设置标记位,某次内循环完成后没有发生任何交换可以直接return
第二次优化:设置每趟内循环最后比较的位置,下一趟内循环只需要循环到这个位置即可
func newBubbleSort(nums []int) {
lenth := len(nums) - 1
tempPosition := 0 //优化内循环次数,只需要循环到最后一次交换的位置即可
for i := 0; i < len(nums)-1; i++ {
flag := 1 //优化标记位 如果某次内循环没有发生比较,说明已经排序完毕,可以直接退出。
for j := 0; j < lenth; j++ {
if nums[j] > nums[j+1] {
nums[j], nums[j+1] = nums[j+1], nums[j]
flag = 0
tempPosition = j
}
}
if flag == 1 {
return
}
lenth = tempPosition
}
}
在优化过后,时间复杂度由On2到最优情况下可以达到On。