排序算法总结 go实现代码

排序算法的分类如下

 

 

各个算法的时间和空间代价如下

注:另外还有一个时间代价为O(N)的桶排序算法,局限性比较大,感兴趣可以另作了解。

 

那么,如何选择各个排序算法呢?

1. 首先,在待排序数组长度比较短的情况下,使用简单排序算法效果比较好。

    在简单排序算法中,直接插入排序的平均情况是相对较好的。而简单选择排序则适合记录(数据元素)的关键字比较大的情况,因为选择排序的移动次数比较少,主要消耗在比较。

2. 如果数组长度比较长,需要考虑改进算法。

   3.希尔排序的平均和最坏情况不如其他改进算法,不用重点考虑。 

   4.如果对稳定性有要求,考虑归并排序。

   5.如果对辅助空间有限制,考虑堆排序

   6.如果没有以上考虑,综合现实使用情况来看,快排算法,如其名,是使用最广泛,效果最好的,选它没错。

 

以下放go代码实现

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
package main
 
import (
    "fmt"
)
 
//BSort0 最简单排序,
func BSort0(nums []int) {
    for i := 0; i < len(nums); i++ {
        for j := i + 1; j < len(nums); j++ {
            if nums[i] > nums[j] {
                nums[i], nums[j] = nums[j], nums[i]
            }
        }
    }
}
 
//BSort1 冒泡排序
func BSort1(nums []int) {
    for i := 0; i < len(nums)-1; i++ {
        for j := len(nums) - 1; j > i; j-- {
            if nums[j] < nums[j-1] {
                nums[j], nums[j-1] = nums[j-1], nums[j]
            }
        }
    }
}
 
//BSort2 优化冒泡,增加了标志变量,如果一次J的循环下来发现没有产生交换,则直接return
func BSort2(nums []int) {
    flag := true
    for i := 0; i < len(nums)-1 && flag; i++ {
        flag = false
        for j := len(nums) - 1; j > i; j-- {
            if nums[j] < nums[j-1] {
                nums[j], nums[j-1] = nums[j-1], nums[j]
                flag = true
            }
        }
    }
}
 
//SelectSort 简单选择排序,类似BSort0的思路,但是保存索引位,以减小交换次数
func SelectSort(nums []int) {
    var minIndex int
    for i := 0; i < len(nums); i++ {
        minIndex = i
        for j := i + 1; j < len(nums); j++ {
            if nums[minIndex] > nums[j] {
                minIndex = j
            }
        }
        nums[i], nums[minIndex] = nums[minIndex], nums[i]
    }
}
 
//InsertSort 插入排序,参考整理扑克
func InsertSort(nums []int) {
    var i, j, temp int
    for i = 1; i < len(nums); i++ {
        temp = nums[i]
        for j = i; j > 0; j-- {
            if temp < nums[j-1] {
                nums[j] = nums[j-1]
            } else {
                break
            }
        }
        nums[j] = temp
    }
}
 
//ShellSort 思路就是将一个长序列,按照h为间隔拆分成多个子序列(交叉拆分),对每个子序列插入排序。不断减小h,重复上述行为。最后h=1再执行一次插入排序。
func ShellSort(nums []int) {
    var i, j, temp int
    n := len(nums)
    h := n/3 + 1
    for h >= 1 {
        //i从h开始,因为每个子序列的第一个元素默认排好了
        for i = h; i < n; i++ {
            temp = nums[i]
            for j = i; j > h-1; j -= h {
                if temp < nums[j-h] {
                    nums[j] = nums[j-h]
                } else {
                    break
                }
            }
            nums[j] = temp
        }
        h /= 3
    }
}
 
//HeapAdjust堆调整函数,默认(s,m]区间内的元素已经满足大顶堆性质,调整nums[s]
func HeapAdjust(nums []int, s int, m int) {
    temp := nums[s]
    for i := 2 * s; i <= m; i *= 2 {
        if i < m && nums[i] < nums[i+1] {
            i++
        }
        if nums[i] < temp {
            break
        } else {
            nums[s] = nums[i]
            s = i
        }
    }
    nums[s] = temp
}
 
//HeapSort 堆排序,将一个堆按层次序编号,编号就是数组索引,将这个堆放在数组中,每次抽走根节点(最小或者最大),剩下的堆从新调整,维持最小顶或者最大顶性质。
//实现最大顶堆排序算法,堆排序比较特殊,数组索引应该从1开始,而不是0。
func HeapSort(nums []int) {
    n := len(nums) - 1
    //初始化,从最底层的非叶节点开始,往上调整
    for i := n / 2; i >= 1; i-- {
        HeapAdjust(nums, i, n)
    }
    //开始排序
    for i := 0; i < n-1; i++ {
        nums[1], nums[n-i] = nums[n-i], nums[1]
        HeapAdjust(nums, 1, n-i-1)
    }
}
 
//归并排序,有递归版本和迭代版本,这里直接实现迭代版本
//Merge 合并[a,b]和[b+1,c]
func Merge(SR []int, TR []int, a int, b int, c int) {
    i, s := a, a
    j := b + 1
    for ; i <= b && j <= c; s++ {
        if SR[i] < SR[j] {
            TR[s] = SR[i]
            i++
        } else {
            TR[s] = SR[j]
            j++
        }
    }
    if i <= b {
        for ; i <= b; i++ {
            TR[s] = SR[i]
            s++
        }
    }
    if j <= c {
        for ; j <= c; j++ {
            TR[s] = SR[j]
            s++
        }
    }
}
 
//MergePass 归并SR中长度为s的子序列,n为序列总长度
func MergePass(SR []int, TR []int, s int, n int) {
    var i int
    for i = 0; i < n-2*s; i += 2 * s {
        Merge(SR, TR, i, i+s-1, i+2*s-1)
    }
    //如果剩余长度大于s,那么合并两个子序列(其中一个长度为s)
    if n-i+1 > s {
        Merge(SR, TR, i, i+s-1, n-1)
    } else {
        for j := i; j < n; j++ {
            TR[j] = SR[j]
        }
    }
}
 
//MergeSort 归并排序,有递归版本和迭代版本,这里直接实现迭代版本
func MergeSort(nums []int) {
    TR := make([]int, len(nums))
    k := 1
    for k < len(nums) {
        MergePass(nums, TR, k, len(nums))
        k *= 2
        if k >= len(nums) {
            nums = TR
            break
        } else {
            MergePass(TR, nums, k, len(nums))
        }
        k *= 2
    }
}
 
//Partition 取nums[low]为中枢值,将[low,high]之间的数分割到左右两边
func Partition(nums []int, low int, high int) int {
    temp := nums[low]
    for low < high {
        //先搜索右边
        for low < high && temp <= nums[high] {
            high--
        }
        nums[low], nums[high] = nums[high], nums[low]
        for low < high && temp >= nums[low] {
            low++
        }
        nums[low], nums[high] = nums[high], nums[low]
    }
    return low
}
 
//Partition2 优化不必要的交换,取nums[low]为中枢值,将[low,high]之间的数分割到左右两边
func Partition2(nums []int, low int, high int) int {
    temp := nums[low]
    for low < high {
        for low < high && nums[high] >= temp {
            high--
        }
        nums[low] = nums[high]
        for low < high && nums[low] <= temp {
            low++
        }
        nums[high] = nums[low]
    }
    nums[low] = temp
    return low
}
 
//QSort 递归快排函数,对[a,b]之间的区域快排
func QSort(nums []int, a int, b int) {
    if a < b {
        q := Partition(nums, a, b)
        QSort(nums, a, q-1)
        QSort(nums, q+1, b)
    }
}
 
//QSort2 尾递归优化版本,基于尾递归的思想,将尾递归转变为迭代,减少初始序列极度不平衡下的函数栈的深度。这种优化主要是为了减小栈空间的利用和切换的时间代价。
func QSort2(nums []int, a int, b int) {
    for a < b {
        q := Partition(nums, a, b)
        QSort2(nums, a, q-1)
        a = q + 1
    }
}
 
//QuickSort 快排入口
func QuickSort(nums []int) {
    QSort2(nums, 0, len(nums)-1)
}
//其它的快排优化:<br>//1.根据数组大小,小于某个阈值时,使用直接插入排序,否则使用快排 <br>//2. 优化选取中枢值,根据数组的大小,随机选择一定数量的数据元,进行排序,选出中间的值作为中枢值<br><br>
func main() {
    tt := []int{5, 6, 9, 4, 7, 3, 1, 8, 2}
    //tt := []int{2, 1, 3, 4, 5, 6, 7}
    QuickSort(tt)
    fmt.Println(tt)
}

  

posted @   布羽  阅读(102)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示