Go语言学习笔记【12】 排序算法之冒泡、选择排序

【声明】

非完全原创,部分内容来自于学习其他人的理论和B站视频。如果有侵权,请联系我,可以立即删除掉。

一、排序算法

所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。

1、相关术语

  • 稳定性:当两个相同的元素同时出现于某个序列之中,则经过一定的排序算法之后,两者在排序前后的相对位置不发生变化。如果 a 原本在 b 的前面,且 a == b,排序之后 a 仍然在 b 的前面,则为稳定排序;否则就是不稳定排序。稳定排序:插入排序,基数排序,归并排序,冒泡排序,计数排序;不稳定排序:快速排序,希尔排序,简单选择排序,堆排序
  • 原地排序:排序过程中不申请多余的存储空间,只利用原来存储待排数据的存储空间进行比较和交换的数据排序。如:希尔排序、冒泡排序、插入排序、选择排序、堆排序、快速排序
  • 时间复杂度:描述该算法的运行时间。通常使用算法的最坏情况的时间复杂度,记为T(n),定义为任何大小的输入n所需的最大运行时间。如果T(n)的上界与输入大小无关,则称其具有常数时间,记作O(1)时间;若T(n) = O( (logn)^k ),则称其具有对数时间;若T(n) = O(k*n) = O(n),则称其具有线性时间;若T(n) = O(M^n) M = O(T(n)) , 则称其具有指数时间;若T(n) = O(n^k),则称其具有k次方时间
  • 空间复杂度:描述算法所耗费的存储空间。

2、主要的排序算法

主要分为:冒泡排序,选择排序,插入排序,希尔排序,归并排序,快速排序,基数排序,堆排序,计数排序,桶排序

二、冒泡排序

1、核心思想和主要方法

  • 核心思想是:把较小的元素往前调或者把较大的元素往后调。
  • 主要方法是:通过对相邻两个元素进行大小的比较,根据比较结果和算法规则对该二元素的位置进行交换,这样逐个依次进行比较和交换,就能达到排序目的。

2、排序的过程

  • 第一趟排序:将第1个和第2个记录的关键字比较大小,如果是逆序的,就将这两个记录进行交换(大的元素往后移),再对第2个和第3个记录的关键字进行比较,依次类推,重复进行上述计算,直至完成第(n-1)个和第n个记录的关键字之间的比较,这样第一趟就可以将最大的元素放在数组的最后面
  • 第二趟排序:将第1个和第2个记录的关键字比较大小,如果是逆序的,就将这两个记录进行交换(大的元素往后移),依次类推,重复进行上述计算,直至完成第(n-2)个和第(n-1)个记录的关键字之间的比较,这样第二趟就可以将第二大的元素放在数组的倒数第二位
  • 重复以上操作,直到第(n-1)趟排序

3、改进的思路(减少趟数)

上述排序过程,固定需要对数组遍历(n-1)次,所耗费的时间较多。
如果在数组中的某一次遍历时,遍历的数组范围就已经有序了,那就说明整个数组已经是有序的,无需继续进行遍历。
按照这个思路,可以进行以下操作:
(1)在数组的每一趟遍历开始时设置一个标志位,初始值为true,假定当前数组是有序的
(2)在数组中元素两两比较中,如果发生了数据交换,则代表不是有序,将标志位设为false;如果没发生交换,则代表当前比较的数据范围是有序的,而剩余的数据则是已排好序的,因此整个数组此时已经是有序的
(3)当前遍历完成之后,根据标志位判断当前数组是否有序,有序则退出遍历

注意:该方法可能可以减少比较的趟数

4、针对数组部分有序的改进(减少比较次数)

分析:假如冒泡排序按照 将较大的元素往后调 的思路
(1)第m趟排序后当前数组正好后面的数据都已排好序(假设是[k+1, n],k < n-m),则按照上一个方法,第m+1趟排序的数组范围是[0, n-m]
(2)如果设置一个索引记录上一次最后发生交换的元素索引k,则第m+1趟排序的数组范围是[0, k],则该范围比[0, n-m]更小,因而可以减少第二层循环的次数,从而减少程序的运行时间

  • 示意图如下:
    在这里插入图片描述
    说明:
    (1)图中,红色的数字表示需要和下一位进行比较,即BubblePlus需要比较5*(9+5)/2 = 35次;BubbleOrdered需要比较9+3+2+1 = 15
    (2)BubbleOrdered排序的说明:
  • 第1趟排序时,[2 1 4 3 0 5 6 7 8 9] --> [1 2 4 3 0 5 6 7 8 9] --> [1 2 3 4 0 5 6 7 8 9] --> [1 2 3 (0) 4 5 6 7 8 9]。注意:由于arr[j]arr[j+1]进行比较,因此0对应的索引3就是第一趟确定的k
  • 第2趟排序时,待排序的数据范围变为了[0, 3],但是由于但是由于数据是两两比较的,因此,4个数据需要比较3次。其他趟依此类推。

5、代码实现

package main

import "fmt"

const size = 10

func Bubble(arr *[size]int) {
   
	var i, j int  //用于两层循环
	cnt := 0      //记录循环/元素比较的次数
	swap_cnt := 0 //记录发生交换的次数
	fmt.Println("原数组序列:  ", *arr)
	for i = 0; i < len(arr)-1; i++ {
   
		/*
			//每次都将较小的元素往前移
			for j = len(arr) - 1; j > i; j-- {
				if arr[j] < arr[j-1] {
					arr[j], arr[j-1] = arr[j-1], arr[j]
					flag = false
				}
			}
		*/
		//每次都将较大的元素往后移
		for j = 0; j < len(arr)-i-1; j++ {
   
			if arr[j] > arr[j+1] {
   
				arr[j], arr[j+1] = arr[j+1], arr[j]
				swap_cnt++
			}
		}
		fmt.Printf("第[%02d]趟: %v\n", i+1, *arr)
		cnt += j
	}
	fmt.Printf("【Bubble】排序的趟数: %d, 排序元素比较的次数: %d, 发生元素交换的次数: %d\n", i, cnt, swap_cnt)
}

func BubblePlus(arr *[size]int) {
   
	var i, j int  //用于两层循环
	var flag bool //设置标志位
	cnt := 0      //记录循环/元素比较的次数
	swap_cnt := 0 //记录发生交换的次数
	fmt.Println("原数组序列:  ", *arr)
	for i = 0; i < len(arr)-1; i++ {
   
		flag = true //假设当前数组是有序的
		//每次都将较大的元素往后移
		for j = 0; j < len(arr)-i-1; j++ {
   
			if arr[j] > arr[j+1] {
   
				arr[j], arr[j+1] = arr[j+1], arr[j]
				swap_cnt++
				flag = false //有元素交换,则表示当前数组无序
			}
		}
		fmt.Printf("第[%02d]趟: %v\n", i+1, *arr)
		cnt += j
		if flag {
   
			break
		}
	}
	fmt.Printf("【BubblePlus】排序的趟数: %d, 排序元素比较的次数: %d, 发生元素交换的次数: %d\n", i+1, cnt, swap_cnt)
}

func BubbleOrdered(arr *[size]int) {
   
	var i, j int                 //用于两层循环
	var flag bool                //设置标志位
	var index int = len(arr) - 1 //记录上一趟排序中最后发生交换的索引
	var tmp int                  //中间变量,记录每一次发生交换时的索引
	cnt := 0                     //记录循环/元素比较的次数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值