经典排序算法
插入排序
插入排序算法的实现原理是在列表当中选择第一个元素,然后进行比较,当后面的元素比前面的元素小时,两元素交换位置,返回以往,当遍历完整个列表时,序列排序完毕: 下面是一个序列的流程图
- 初始状态: [5, 2, 4, 6, 1, 3]
- 第一次迭代: [2, 5, 4, 6, 1, 3]
- 第二次迭代: [2, 4, 5, 6, 1, 3]
- 第三次迭代: [2, 4, 5, 6, 1, 3]
- 第四次迭代: [1, 2, 4, 5, 6, 3]
- 第五次迭代: [1, 2, 3, 4, 5, 6]
插入排序在数据基本有序的情况情况下时间复杂度最低是O(n),平均是O(n^2),最坏是O(n^2) 算法实现:
package main
import "fmt"
func insertsort(arr []int) {
n := len(arr)
for i := 1; i < n; i++ {
key := arr[i]
j := i - 1
for j >= 0 && arr[j] > key {
arr[j+1] = arr[j]
j--
}
arr[j+1] = key
}
}
func main() {
arr := []int{5, 2, 4, 6, 1, 3}
insertsort(arr)
fmt.Println("Sorted array:", arr)
}
快速排序
快速排序(Quick Sort)是一种常用的排序算法,它使用分治法(Divide and Conquer)策略来将一个数组分成两个子数组,然后递归地对子数组进行排序,最终将整个数组排序完成。 下面一组示例的演化过程:
- 初始状态 :9, 5, 7, 2, 1, 8, 3, 6, 4
- 第一次:4,5,7,2,1,8,3,6,9//基于第一个数字9进行第一次演化
- 第二次:3,2,1,4,7,8,5,6,9//基于4进行演化,将比他小的放左边,大的放右边
- 第三次:1,2,3,4,7,8,5,6,9//基于3进行演化,将比他小的放左边,大的放右边
- 第四次:1,2,3,4,7,8,5,6,9//基于1进行演化,将比他小的放左边,大的放右边
- 第五次:1,2,3,4,6,5,7,8,9//基于7进行演化,将比他小的放左边,大的放右边
- 第五次:1,2,3,4,5,6,7,8,9//基于6进行演化,将比他小的放左边,大的放右边
快速排序最好情况时间复杂度:O(n log n),最坏情况时间复杂度:O(n^2)(当选择枢轴不平衡时)平均情况时间复杂度:O(n log n) 实现代码
func quicksort(arr []int) {
if len(arr) <= 1 {
return
}
pivot := arr[0]
low := 1
high := len(arr) - 1
for low <= high {
if arr[low] <= pivot {
low++
} else {
arr[low], arr[high] = arr[high], arr[low]
high--
}
}
arr[0], arr[low-1] = arr[low-1], arr[0]
quicksort(arr[:low-1])
quicksort(arr[low:])
}
堆排序
堆排序是利用构建最大堆或者是最小堆,交换根节点和最后一个节点:将堆的根节点与最后一个叶子节点交换,然后将最后一个节点排除在堆之外,相当于将最大值移到了已排序区域。然后在重新整理堆,再进行之前的操作,反复以往,当最后只剩最后一个时,完成对于排序列表的输出。
堆排序最好情况时间复杂度:O(n log n),最坏情况时间复杂度:O(n log n),平均情况时间复杂度:O(n log n)
package main
import "fmt"
// 调整堆,使其满足最大堆的性质
func heapify(arr []int, n int, i int) {
largest := i
left := 2*i + 1
right := 2*i + 2
if left < n && arr[left] > arr[largest] {
largest = left
}
if right < n && arr[right] > arr[largest] {
largest = right
}
if largest != i {
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
}
}
// 堆排序
func heapSort(arr []int) {
n := len(arr)
// 构建最大堆
for i := n/2 - 1; i >= 0; i-- {
heapify(arr, n, i)
}
// 交换根节点和最后一个节点,重建最大堆
for i := n - 1; i > 0; i-- {
arr[0], arr[i] = arr[i], arr[0]
heapify(arr, i, 0)
}
}
func main() {
arr := []int{5, 2, 4, 6, 1, 3}
fmt.Println("Unsorted array:", arr)
heapSort(arr)
fmt.Println("Sorted array:", arr)
}
pdqsort
pdqsort算法是基于现有排序算法下,进行改进得到的算法,他将插入排序,快速排序,堆排序进行结合起来,使其在合适的阶段选择合适的排序算法,使得算法在何时都能保持在最差都有O(n log n)的时间复杂度,在情况好的时候甚至可以到O(n)
pdqsort的排序实现流程图:
但是第一版当中存在一些问题,比如说pivot的选择会导致效果不佳,如果使用首个,又或者遍历整个数组找到中位数,这又耗费更多的算力。 所以在下一个版本进行了pivot优化:
总结
- 升级pivot选择策略(近似中位数)
- 发现序列可能逆序,则翻转序列->应对reverse场景
- 发现序列可能有序,使用有限插入排序->应对sorted场景