【排序算法】十大排序算法之冒泡排序
冒泡排序是数据结构和算法课程中最基础的排序算法,通过循环比较相邻的两个元素的大小,按照预设的升序或降序交换不符合排序规则的相邻元素,十分简单有效。
整个排序过就好像气泡不断从水里冒出来,最大的先出来,次大的第二出来,最小的最后出来,所以将这种排序方式称为冒泡排序(Bubble Sort)。
一、复杂度分析
- 时间复杂度
时间复杂度:定量描述了算法的运算时间,用大O符号来表示。
在计算的时候,先找出算法基本操作的执行次数,即为T(n),然后找到它的同数量级,即为f(n),如果T(n)与f(n)的比值求极限可得到一个常数c,那么时间复杂度T(n)=O(f(n))。
冒泡排序的时间复杂度为两个嵌套 for 循环的时间复杂度:T(n) = O(n*n)
- 空间复杂度
空间复杂度:是运行完一个程序所需要的内存的大小。
此处所占空间包括了存储算法本身所需要的空间,输入与输出数据所占空间,以及一些临时变量(比如上面的h)所占用的空间。一般而言,我们只比较额外空间,来比较算法的空间优越性。
冒泡排序的临时变量所占空间不随处理数据n的大小改变而改变,则空间复杂度为O(1)。
二、算法步骤
从第一个元素开始;
比较相邻元素,如果第一个元素大于第二个元素,则交换两元素;
依此执行步骤 2 。最后的元素会是最大的数。
对剩余的元素循环执行步骤1、2、3;
直到没有可交换的元素为止,则排序结束;
三、图示
借用网络动图;
四、源代码(go语言实现)
实现了最简单的冒泡,和添加标识位的冒泡;
package main
import (
"fmt"
"time"
)
// 最简排序
func bubbleSort1(order []int) {
defer calcTime(time.Now())
order_len := len(order)
if order_len < 2 {
return
}
for i := 0; i < order_len-1; i++ {
for j := 0; j < order_len-i-1; j++ {
if order[j] > order[j+1] {
order[j], order[j+1] = order[j+1], order[j]
}
fmt.Printf("<%d-%d> %v\r\n", i, j, order)
}
}
}
// 添加 升降序控制、排序完成标识
func bubbleSort2(order []int, asc bool) {
defer calcTime(time.Now())
order_len := len(order)
if order_len < 2 {
return
}
flag_order := true // 已排序完成标识
for i := 0; i < order_len-1; i++ {
for j := 0; j < order_len-i-1; j++ {
if (asc && order[j] > order[j+1]) || (!asc && order[j] < order[j+1]) {
order[j], order[j+1] = order[j+1], order[j] // 这种交换方式真是方便
flag_order = false
} else {
continue
}
fmt.Printf("<%d-%d> %v\r\n", i, j, order)
}
if flag_order {
return
}
flag_order = true
}
}
func main() {
order := []int{12, 34, 0, 6, 89, 67, 78, 56}
bubbleSort1(order)
order = []int{12, 34, 0, 6, 89, 67, 78, 56}
fmt.Printf("<%d-%d> %v\r\n", 0, 0, order)
bubbleSort2(order, true)
}
// 计算函数执行事件
func calcTime(start time.Time) {
fmt.Printf("===【cost time: %v】===\r\n", time.Since(start))
}
五、输出结果
- 打印了每次交换元素后的序列,以观察具体的元素交换细节;
- 同时打印两个排序函数的执行时间,用作对比;
# 执行结果
# 添加控制之后的冒泡函数在序列相对整齐的情况下,消耗时间明显短于普通的冒泡函数
<0-0> [12 34 0 6 89 67 78 56]
<0-1> [12 0 34 6 89 67 78 56]
<0-2> [12 0 6 34 89 67 78 56]
<0-3> [12 0 6 34 89 67 78 56]
<0-4> [12 0 6 34 67 89 78 56]
<0-5> [12 0 6 34 67 78 89 56]
<0-6> [12 0 6 34 67 78 56 89]
<1-0> [0 12 6 34 67 78 56 89]
<1-1> [0 6 12 34 67 78 56 89]
<1-2> [0 6 12 34 67 78 56 89]
<1-3> [0 6 12 34 67 78 56 89]
<1-4> [0 6 12 34 67 78 56 89]
<1-5> [0 6 12 34 67 56 78 89]
<2-0> [0 6 12 34 67 56 78 89]
<2-1> [0 6 12 34 67 56 78 89]
<2-2> [0 6 12 34 67 56 78 89]
<2-3> [0 6 12 34 67 56 78 89]
<2-4> [0 6 12 34 56 67 78 89]
<3-0> [0 6 12 34 56 67 78 89]
<3-1> [0 6 12 34 56 67 78 89]
<3-2> [0 6 12 34 56 67 78 89]
<3-3> [0 6 12 34 56 67 78 89]
<4-0> [0 6 12 34 56 67 78 89]
<4-1> [0 6 12 34 56 67 78 89]
<4-2> [0 6 12 34 56 67 78 89]
<5-0> [0 6 12 34 56 67 78 89]
<5-1> [0 6 12 34 56 67 78 89]
<6-0> [0 6 12 34 56 67 78 89]
===【cost time: 217.854µs】===
<0-0> [12 34 0 6 89 67 78 56]
<0-1> [12 0 34 6 89 67 78 56]
<0-2> [12 0 6 34 89 67 78 56]
<0-4> [12 0 6 34 67 89 78 56]
<0-5> [12 0 6 34 67 78 89 56]
<0-6> [12 0 6 34 67 78 56 89]
<1-0> [0 12 6 34 67 78 56 89]
<1-1> [0 6 12 34 67 78 56 89]
<1-5> [0 6 12 34 67 56 78 89]
<2-4> [0 6 12 34 56 67 78 89]
===【cost time: 99.877µs】===
Process finished with exit code 0
六、总结
冒泡排序是一种简单、易理解的排序算法,对理解程序的时间复杂度和空间复杂度很有帮助,也有助于理解其它排序算法;
特别是在毕业入职面试中,是面试官很喜欢问出的一道题目。
——2019-02-28——