引言
想到排序,是否就跳出了数据结构课上的记忆,基数排序,堆排序,快速排序,插入冒泡排序,希尔排序...
对于这些算法,往往课堂上只是抛出了算法实现方式,分析时间复杂度,空间复杂度,稳定性,也可能有部分改进算法,考察就是硬问这几种排序,没有情景,要求写哪种排序算法就是哪种. 学校很纯粹,但生活不一定. 之前的go实战,需求上线的字节课程已经见识了防御编程,分布式,微服务架构的开发流程,所以排序算法是面向生产的,必须考虑效率和成本!
直观感受下排序的工程选择
实践
首先是熟悉的插入算法InsertionSort
func insertionSort(data interface ,a,b int){
for i := 0; i < b; i++{
for j := i ; j > a&&data.Less(j,j-1) ; j--{
data.Swap(j , j-1)
}
}
}
根据基准测试,短序列采用插入排序比较快.根据工程实践经验,字节长度24内算短序列.
堆排序:很稳定的算法heapSort
func siftDown(data Interface, lo, hi, first int) {
root := lo
for {
child := 2*root + 1
if child >= hi {
break
}
if child+1 < hi && data.Less(first+child, first+child+1) {
child++
}
if !data.Less(first+root, first+child) {
return
}
data.Swap(first+root, first+child)
root = child
}
}
func heapSort(data Interface, a, b int) {
first := a
lo := 0
hi := b - a
// Build heap with greatest element at top.
for i := (hi - 1) / 2; i >= 0; i-- {
siftDown(data, i, hi, first)
}
// Pop elements, largest first, into end of data.
for i := hi - 1; i >= 0; i-- {
data.Swap(first, first+i)
siftDown(data, lo, i, first)
}
}
升序排序采用堆排序的基本原理是:建立一个小顶堆(就是父节点始终比子节点小的完全二叉树),每次把堆顶的元素取出来就成了一个升序的序列. 代码中创建堆使用循环中调用shiftdown函数,即从最后一个叶节点向上遍历,不断调整.
快速排序 QuickSort
func quickSort(arr []int, left int, right int) {
if left < right {
pivotIndex := partition(arr, left, right)
quickSort(arr, left, pivotIndex-1)
quickSort(arr, pivotIndex+1, right)
}
}
//
func partition(arr []int, left int, right int) int {
pivot := arr[right]
i := left - 1
for j := left; j < right; j++ {
if arr[j] < pivot {
i++
arr[i], arr[j] = arr[j], arr[i]
}
}
i++
arr[i], arr[right] = arr[right], arr[i]
return i
}
其中,quickSort函数采用递归的方式进行快速排序,partition函数用于将数组划分为左右两部分。在partition函数中,选取数组最后一个元素作为pivot元素,并将小于pivot元素的元素放在数组左侧,大于pivot元素的元素放在数组右侧。最后返回pivot元素的位置即可。
最后来个总结
mindmap
sort
插入n n^2
快速
堆排序Heap稳!
pdq混合排序算法
情况
随机序列
短序列数据插入排序
其他用快排
总结:
- pdq从选择到优化
- 使用插入排序短序列
- 极端情况堆排序 保证算法可行性
- 完全随机的情况 pivot取近似中位数
然后呢...
- 重复数很多的情况
- 新的pdq排序算法 实现第一个版本...
人生三省,吾常思图省身之事 下篇文章见🍻