不同的排序方式,如有错误请评论指出,我会及时更改
冒泡排序及其优化
冒泡排序 稳定排序,平均复杂O(n^2) 最优 O(n)
//冒泡排序及其优化,优化后最优解可以O(n),
//初始版本
func Bubble(array []int) {
//次数小于长度-1,因为内层循环会+1
for i:=0;i<len(array)-1;i++{
for j:=0;j<len(array)-i-1;j++{
if array[j]>array[j+1]{
array[j],array[j+1] = array[j+1],array[j]
}
}
}
}
//优化方式1,增加flag判断
func BubbleOne(array []int) {
flag:=false
for i:=0;i<len(array)-1;i++{
flag = false
for j:=0;j<len(array)-i-1;j++{
if array[j]>array[j+1]{
array[j],array[j+1] = array[j+1],array[j]
flag = true
}
}
//未执行则已经有序
if !flag{
return
}
}
}
//优化方式2,在1的基础上
//增加position记录最后交换的位置j,下次不再判断后面的数字
func Bubbletwo(array []int) {
//加入flag,判断是否提前结束
flag:=false
length:=len(array)-1
//记录最后一次交换的位置,后面没有交换,为有序
position:=0
for i:=0;i<len(array)-1;i++{
flag = false
for j:=0;j<length;j++ {
if array[j]>array[j+1]{
array[j],array[j+1] = array[j+1],array[j]
flag = true
position = j//记录最后一次交换次数
}
fmt.Printf("外层%d,内层%d\n",i,j)
}
if !flag{
break
}
length = position
}
}
选择排序
//选择排序,选择最小和开始交换
好坏平均复杂度 O(n^2) 不稳定
func SelectSort(array []int) {
index:=0
for i:=0;i<len(array)-1;i++{
index = i
for j:=i+1;j<len(array);j++{
//比最小的小,则更新最小index
if array[index]>array[j]{
index = j
}
}
//交换
array[i],array[index]=array[index],array[i]
}
}
插入排序
插入排序 稳定排序 时间O(n^2)
func Inserts(nums []int,n int) {
var j int
for i:=1;i<n;i++{
if nums[i]<nums[i-1]{
//保存临时数据,数组数据后移
tmp:=nums[i]
//注意边界 j>0
for j=i;j>0&&nums[j-1]>tmp;j--{
nums[j] = nums[j-1]
}
nums[j] = tmp
}
}
}
快排及其优化
快排 不稳定排序 O(nlogn) 还有多路快排优化
- 初始版本
//j = len(array)-1
func Fast(array []int,i,j int) {
left:=i
right:=j
//超出范围 退出,递归停止条件
if left>right{
return
}
tmp:=array[left]
for left<right{
for left<right&&array[right]>=tmp{
right--
}
array[left] = array[right]
for left<right&&array[left]<=tmp{
left++
}
array[right] = array[left]
}
array[left] = tmp
Fast(array,i,left-1)
Fast(array,left+1,j)
}
- 增加随机数
func sortArray(nums []int) []int {
fast(nums,0,len(nums)-1)
return nums
}
// 随机
func fast(nums []int,st,end int){
l,r:=st,end
if l>r{
return
}
//增加随机数 [0,2),交换开始和idx
// import "math/rand"
idx := st+rand.Intn(end-st+1)
nums[idx],nums[l] = nums[l],nums[idx]
tmp:=nums[l]
for l<r{
for l<r&&nums[r]>=tmp{
r--
}
nums[l] = nums[r]
for l<r&&nums[l]<=tmp{
l++
}
nums[r] = nums[l]
}
nums[l] = tmp
fast(nums,st,l-1)
fast(nums,l+1,end)
}
- 三路快排
三路快排,分三部分,1小于tmp部分,等于tmp部分,大于tmp部分
2022.3.15 全新版本
//数组排序
func sortArray(nums []int) []int {
fast(nums,0,len(nums)-1)
return nums
}
func fast(nums []int,low,high int){
//三路快排思想,左右互换 适合重复元素过多
if low>high{
return
}
l,r:=low,high
i:=low //初始为low
tmp:=nums[l]
for i<=r{ //i==r还是需要判断互换位置
if nums[i]<tmp{
nums[i],nums[l]=nums[l],nums[i]
i++
l++
}else if nums[i]>tmp{ //not i++ because nums[r] is certain
nums[r],nums[i]=nums[i],nums[r]
r--
}else{
i++
}
}
fast(nums,low,l-1) //包含下标,所以l位置是不符合<tmp
fast(nums,r+1,high)//r不符合>tmp
}
归并排序
稳定排序 时间O(nlogn) 空间O(n) 有多路归并
归并可以结合很多其他知识 链表,树等等,分治思想很重要(至少面试会问)
海量数据排序可以使用多路归并
func MergeSort(array []int)[]int {
//单个数据时返回,递归停止条件
if len(array)<2{
return array
}
//递归分割左右,想好递归停止的条件,分治
mid:=len(array)/2
left:=MergeSort(array[:mid])
right:=MergeSort(array[mid:])
return merge(left,right)
}
//合并两个有序链表类似的做法
func merge(left,right []int) []int {
//额外的返回空间
res:=make([]int,0)
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++
}
}
//这里偷懒了,可以自己写详细些
/*if i==len(left){
res = append(res,right[j:]...)
}else{
res = append(res,left[i:]...)
}*/
//详细写法
//left right 其中一个没有结束
if i<len(left){
for i<len(left){
res = append(res,left[i])
i++
}
}else{
for j<len(right){
res = append(res,right[j])
j++
}
}
return res
}
希尔排序
间隔插入排序 懒得写了
堆排序
堆排序 不稳定 时间复杂O(nlogn)
建堆时间复杂度为O(n) 计算方式参考
topk问题,优先队列实现,建议手动写堆写下合并链表的题目
leetcode 合并k个排序链表,最优解使用优先队列,和堆有关
func HeapSort (array []int) {
//建堆,从后向前建立堆
for i:=len(array)/2-1;i>=0;i--{
sink(array,i,len(array))
}
for i:=len(array)-1;i>=0;i--{
array[0],array[i] = array[i],array[0]
sink(array,0,i-1)
}
}
//下沉,堆排序最关键堆核心
//想得到从小到大的数据,需要大顶堆,最大放在结尾,最后是小到大顺序
func sink(array []int,start ,length int) {
for{
next:=2*start+1
//超出范围 退出循环
if next>length{
break
}
//找最大的值和上层互换 (next+1<=length不是next+1<length) 因为length不会越界
if next+1<=length&&array[next+1]>array[next]{
next++
}
//上层已经大于下面,不用下沉,直接退出循环
if array[start]>array[next]{
break
}
//互换后继续寻找,可能继续下沉
array[start],array[next] = array[next],array[start]
start = next
}
}
- 另外一种堆排写法,差不多
func sortArray(nums []int) []int {
return heapsort(nums)
}
func heapsort(nums []int)[]int{
//从后向前建堆
end:=len(nums)-1
for i:=end/2;i>=0;i--{
sink(nums,i,end)
}
//顶部最大,跟尾部互换长度减小
for i:=end;i>=0;i--{
nums[0],nums[i] = nums[i],nums[0]
end--
sink(nums,0,end)
}
return nums
}
func sink(nums []int,root int,end int){
for {
child:=2*root+1
if child>end{
return
}
if child<end&&nums[child]<=nums[child+1]{ //可以等于end
child++
}
if nums[root]>nums[child]{
return
}
//互换,下沉
nums[root],nums[child] = nums[child],nums[root]
root = child
}
}
自行验证
func main() {
array:=[]int{2,1,5,8,5,6,3,2,7}
//arra:=[]int{2,1,5,8}
//insertSort(array,9)
//Fast(array,0,8)
//CreateHeap(array)
//Inserts(array,9)
//res:=MergeSort(array)
HeapSort(array)
fmt.Println(array)
fmt.Println(array[:len(array)])
}
数组中第k大元素
- 堆排序方式,大顶堆移除堆顶k-1次
func findKthLargest(nums []int, k int) int {
if len(nums)==0{
return -1
}
for i:=len(nums)-1;i>=0;i--{ //建堆,从i处下沉
sink(nums,i,len(nums)-1)
}
//i>len(nums)-1 移动0次,则移动k-1次需要i>len(nums)-1-(k-1)
for i:=len(nums)-1;i>len(nums)-k;i--{
nums[0],nums[i] = nums[i],nums[0]
sink(nums,0,i-1)
}
return nums[0]
}
func sink(nums []int,st,length int){
for {
next:=2*st+1
if next>length{
break
}
if next+1<=length&&nums[next+1]>nums[next]{
next++
}
if nums[st]>nums[next]{
break
}
nums[st],nums[next] = nums[next],nums[st]
st = next
}
}
- 快排方式,不需要完全排序,每次只排一遍
时间复杂度O(n)
func findKthLargest(nums []int, k int) int {
if len(nums)==0{
return -1
}
l,r:=0,len(nums)-1
for l<=r{ //需要越界为结束条件
index:=fast(nums,l,r)
if index>k-1{
r = index-1 //注意+1和-1问题
}else if index<k-1{
l = index+1
}else{
return nums[k-1]
}
}
return -1
}
func fast(nums []int,left,right int)int{
tmp:=nums[left]
for left<right{
for left<right&&nums[right]<=tmp{
right--
}
nums[left] = nums[right]
for left<right&&nums[left]>=tmp{
left++
}
nums[right] = nums[left]
}
nums[left] = tmp
return left
}