排序之冒泡排序|Go主题月

841 阅读4分钟

排序:按照某种顺序排列数据。
例如:一组单词按照字母顺序排序;一组单词按照单词长度进行排序。

排序的条件:

  1. 元素之间可以按某种规则比较大小;
  2. 明确是否违反顺序

假设要求切片数组[10, 1, 5, 3]数据按照升序排列, 以冒泡排序算法作为排序算法

  1. 冒泡排序多次遍历切片数组。它比较相邻的元素,将不合顺序元素交换位置。
  2. 每一轮遍历都将下一个最大值放到正确的位置上。本质上,每个元素通过“冒泡”找到自己所属的位置。
  3. 在按升序的顺序排序中,把大的元素不断的往后移动,这样每次遍历结束,大的数据都放到了正确的位置。

第一轮遍历时,需要遍历所有的元素,具体见下图(带颜色方框的表示正在比较的元素):

  1. 首先从0号元素开始遍历,0号元素即值为10和1号元素即值为1进行比较,因为10大于1,所以需要互相交换位置;
  2. 此时,切片数组为[1, 10, 5, 3],现在遍历到了1号元素,把1号元素即值为10和2号元素即值为5进行比较,因为10大于5,所以需要互相交换位置;
  3. 此时,切片数组为[1, 5, 10, 3],现在遍历到了2号元素,把2号元素即值为10和3号元素即值为3进行比较,因为10大于3,所以需要互相交换位置;
  4. 此时,切片数组为[1, 5, 3, 10],第一轮遍历结束,最大的数字已经位于正确的位置。

冒泡第一轮.png

第二轮遍历时,因为最后一个元素已经处于正确的位置,所以只需要遍历0号元素到倒数第二个元素即可,即[1, 5, 3],具体见下图(带颜色方框的表示正在比较的元素):

  1. 此时,切片数组为[1, 5, 3, 10], 首先从0号元素开始遍历,0号元素即值为1和1号元素即值为5进行比较,因为1小于10,无需交换位置;
  2. 此时,切片数组为[1, 5, 3, 10],现在遍历到了1号元素,把1号元素即值为5和2号元素即值为3进行比较,因为5大于3,所以需要互相交换位置;
  3. 此时,切片数组为[1, 3, 5, 10],第二轮遍历结束,第二大的数值已处于正确的位置。

冒泡第二轮.png

第三轮遍历时,因为经过前两轮的排序,目前只需遍历[1, 3]即可,具体见下图(带颜色方框的表示正在比较的元素):

  1. 此时,切片数组为[1, 3, 5, 10],首先从0号元素开始遍历,0号元素即值为1和1号元素即值为3进行比较,因为1小于3,无需交换位置;
  2. 此时,切片数组为[1, 3, 5, 10], 第三轮遍历结束。

冒泡第三轮.png

整个切片数值已经按升序排序完毕为[1, 3, 5, 10]。

时间复杂度: O(n²)
从上面的排序过程中,给n个元素的切片数值冒泡排序需要经过n-1次遍历,然后每次遍历的比较次数依次为n-1、n-2、n-3、...、2、1,所以是1+2+3+...+n-2+n-1的和,即½n²-½n,即O(n²)。

使用Golang实现的冒泡排序代码如下:

package main

import (
	"fmt"
)

func bubbleSort(numList []int) {
	for needToSortNums := len(numList); needToSortNums > 0; needToSortNums-- {
		// 标记内部循环是否发生了元素交换
		swap := false
		for idx := 0; idx < needToSortNums-1; idx++ {
			if numList[idx] > numList[idx+1] {
				temp := numList[idx]
				numList[idx] = numList[idx+1]
				numList[idx+1] = temp
				swap = true
			}
		}
		// 如果没有发生元素交换,说明已经是正确的顺序,函数退出
		if !(swap) {
			return
		}
	}
}

func main() {
	//numList := []int{10, 1, 5, 3}
	numList := []int{10, 1, 5, 3, 2, 7}
	//numList := []int{10, 1, 5, 3}
	fmt.Println(numList)
	// [10 1 5 3]
	bubbleSort(numList)
	fmt.Println(numList)
	// [1 3 5 10]
}

选择排序为在冒泡排序基础上进行优化后的一种排序算法。