排序算法

介绍

​ 在面试中被问到排序算法,发现不能很好的描述出来,于是写一篇文章总结一下。常见的排序算法有 冒泡排序插入排序快速排序归并排序堆排序等。下面一一介绍。

1、冒泡排序 O(n^2)

算法思想:

​ 共 n 个元素需要排序,通过比较相邻的两个元素,并将较大的元素放到后面,以此每一轮比较将乱序的元素中的最大的元素放到最后一个有序区间中。每一次遍历都能确定一个数在有序数组中的位置,且数组最后的一个区间中的数是有序的,直到所有数的位置都确定了,排序完成。

核心代码:
1
2
3
4
5
6
7
8
for i := 0; i < n-1; i++ {
for j := 0; j < n-i-1; j++ {
if arr[j] > arr[j+1] {
// 交换 arr[j] 和 arr[j+1]
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}

2、插入排序 O(n^2)

算法思想:

​ 遍历一遍整个数组,每次遍历将当前元素放到数组首部的有序序列中的合适位置,每次保证数组首部增加一个数,遍历 n - 1 次后,就可以保证整个数组的有序性。【合适的位置:往前找第一个小于当前数的位置,将其放在该数的后面,就说明当前数前面小于当前数,当前数后面大于当前数】

核心代码:
1
2
3
4
5
6
7
8
9
for i := 1; i < n; i ++ {
key := q[i] // go 中切片使用的注意点
j := i - 1 // 往前找第一个小于当前数的位置,将其放在该数的后面,就说明当前数前面小于当前数,当前数后面大于当前数
for j >= 0 && q[j] > key {
q[j + 1] = q[j]
j --
}
q[j + 1] = key
}

3、快速排序 O(nlogn)

算法思想:

​ 分而治之的一个思想,每次将整个数组分为两部分,然后保证左边是小于某个值的,右边是大于某个值的,这样在对两个部分采用同样的思想去划分,最后划分到区间中只有一个数了,就可以保证整个数组是有序的了。

核心代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func quickSort(q []int, l, r int) {
if l >= r {
return
}

mid := (l + r) >> 1
x := q[mid]
i, j := l - 1, r + 1
for i < j {
for i ++; q[i] < x; i ++{}
for j --; q[j] > x; j --{}
if i < j {
q[i], q[j] = q[j], q[i]
}
}
quickSort(q, l, j)
quickSort(q, j + 1, r)
}

4、归并排序 O(nlogn)

算法思想:

​ 利用递归将整个区间分为足够小的区间,保证小区间中的数是有序的,然后再逐渐回溯,使大区间也变得有序。最终达到整个区间有序的一个状态。

核心代码:
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
unc mergeSort(a []int, l, r int) {
if l >= r {
return
}

mid := (l + r) >> 1
mergeSort(a, l, mid)
mergeSort(a, mid+1, r)
i, j, k := l, mid+1, 0
for i <= mid && j <= r {
if a[i] <= a[j] {
t[k] = a[i]
k++
i++
} else {
t[k] = a[j]
k++
j++
}
}

for i <= mid {
t[k] = a[i]
k++
i++
}
for j <= r {
t[k] = a[j]
k++
j++
}
for i, j := l, 0; i <= r; i++ {
a[i] = t[j]
j++
}
}

5、堆排序

算法思想

​ 堆:即一棵完全二叉树,且每一个点小于等于其左右儿子节点【小根堆】。

​ 建堆:从总数的 n/2 开始往上建,因为必须保证其有左右儿子节点。建堆的过程,就是把父节点、左右儿子节点中较小的元素放到父节点的位置,逐步将大的节点往下移的操作。

核心代码
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
package main


import "fmt"

var n, m, cnt int
var h []int

func down (u int) {
t := u
if u * 2 <= cnt && h[u * 2] < h[t] {
t = u * 2
}
if u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t] {
t = u * 2 + 1
}
if t != u {
h[t], h[u] = h[u], h[t]
down(t)
}
}

func main() {
fmt.Scan(&n, &m)
h = make([]int, n + 1)

for i := 1; i <= n; i ++ {
fmt.Scan(&h[i])
}
cnt = n

for i := n / 2; i > 0; i -- {
down(i)
}
for n > 0 {
n --
fmt.Printf("%d ", h[1])
h[1] = h[cnt]
cnt --
down(1)
}
}

其余算法慢慢补充。。。。


排序算法
http://example.com/2023/10/14/工具/排序算法/
作者
Feng Tao
发布于
2023年10月14日
更新于
2023年10月16日
许可协议