【算法学习-排序算法】冒泡排序

164 阅读4分钟

冒泡排序

冒泡排序是我接触算法的第一个算法。😶😶😶

算法介绍

属于一种比较简单的算法,先来看看他的排序流程:

现在有有一组乱序的数,比如:5 9 1 6 8 14 6 49 25 4 6 3

  • 第一次迭代:从第一个数开始,依次比较相邻的两个数,如果前面的数比后面的数大,那么两个数会交换位置,直到处理到了倒数第二位时候,现在的最后一位就是最大的数 49 [5 1 6 8 9 6 14 25 4 6 3 49]
  • 第二次迭代:与第一次迭代工作一致。[1 5 6 8 6 9 14 4 6 3 25 49]
  • 9 次迭代后:….
  • 第十一次:[1 3 4 5 6 6 6 8 9 14 25 49],这就得出来排序好的一组元素了。

你会发现大的元素会像泡泡一样浮到顶端,就像可乐里面气泡一样,所以这也是为什么叫做冒泡排序的原因。

为了更好的展示过程,采用动画方式取展示上面所有的具体的流程。

GIF 2021-12-21 14-13-05

算法实现

package main

import "fmt"

func BubbleSort(list []int)  {
	n:=len(list)
	for i:=n-1;i>0;i--{
		for j:=0;j<i;j++{
			if list[j]>list[j+1]{
				list[j],list[j+1]=list[j+1],list[j]
			}
		}
	}
}
func main()  {
	//排序算法
	list := []int{5, 9, 1, 6, 8, 14, 6, 49, 25, 4, 6, 3}
    //冒泡排序
	BubbleSort(list)
	fmt.Println(list)
}

时间复杂度

当一组元素个数为 N,迭代过程中进行比较的次数:

  • 第一次迭代比较次数:N-1
  • 第二次迭代比较次数:N-2
  • 第三次迭代比较次数:N-3
  • …(省略多次迭代)
  • N-1 次(也是最后一次)迭代比较次数:1

总体的比较次数:1+2+3+4+...+N-1=(N^2 - N)/2,这是平方级别的时间复杂度,我们记为 O(n^2)

空间复杂度

很多编程语言不允许这样:list[j], list[j+1] = list[j+1], list[j],会要求交换两个值时必须建一个临时变量 a 来作为一个过渡,如:

    a := list[j+1]
    list[j+1] = list[j]
    list[j] = aCopy to clipboardErrorCopied

但是 Golang 允许我们不那么做,它会默认构建一个临时变量来中转。

这里只用到了常量进行交换,所以空间复杂度:O(1)

算法改进

怎么去改进这个算法,减少他的时间复杂度呢?

[5 1 6 8 9 6 14 25 4 6 3 49]
[1 5 6 8 6 9 14 4 6 3 25 49]
[1 5 6 6 8 9 4 6 3 14 25 49]
[1 5 6 6 8 4 6 3 9 14 25 49]
[1 5 6 6 4 6 3 8 9 14 25 49]
[1 5 6 4 6 3 6 8 9 14 25 49]
[1 5 4 6 3 6 6 8 9 14 25 49]
[1 4 5 3 6 6 6 8 9 14 25 49]
[1 4 3 5 6 6 6 8 9 14 25 49]
[1 3 4 5 6 6 6 8 9 14 25 49]
[1 3 4 5 6 6 6 8 9 14 25 49]

通过上面的打印,发现好像多进行一次的比较,我们可以通过引入了 didSwap 的变量,记录当前这一轮中是否存在交换数值的情况,代表此轮其实已经是有序的一组数了,就可以退出不用继续比较了。

func BubbleSort2(list []int) {
	n := len(list)
	// 在一轮中有没有交换过
	didSwap := false

	// 进行 N-1 轮迭代
	for i := n - 1; i > 0; i-- {
		// 每次从第一位开始比较,比较到第 i 位就不比较了,因为前一轮该位已经有序了
		for j := 0; j < i; j++ {
			// 如果前面的数比后面的大,那么交换
			if list[j] > list[j+1] {
				list[j], list[j+1] = list[j+1], list[j]
				didSwap = true
			}
		}

		// 如果在一轮中没有交换过,那么已经排好序了,直接返回
		if !didSwap {
			return
		}
	}
}

时间复杂度

最好的情况:这组数是有序的状态,我只需要判断一轮,确定是有序的状态,后续就不用比较了O(n)

最坏的情况:这组数是乱序的状态,每次比较都要交换值,O(n^2)

空间复杂度

其实没有改变,O(1)

参考文章

冒泡排序