排序算法详解(一)Go

一、什么是排序算法?

生活中经常有需要我们排序的栗子,比如说成绩排名,按照分数从高到底排序,对绝大多数程序员来说,排序算法可能是学过的第一种算法,但是不同的排序算法是存在差异的,有的算法花费时间很长,有的算法花费空间很多,就好比我们的代码质量,不同的算法得到的程序性能是不一样的。因此我们要学习下优秀的算法,用一种更合适的方式去处理问题。

二、如何分析排序算法的好坏?

主要是有以下几类指标。

2.1 排序算法的执行效率
2.1.1 最好情况

指的是在一个算法在最好的情况下的执行效率,比如说要排序的数组刚好是已经有序的,要吃饭的时候刚好外卖到了。当然这是理想状态下,不可强求。

2.1.2 最坏情况

指的是在最糟糕的情况下,执行这段代码的时间复杂度,比如要排序的数组刚好都是反序的,赚到钱的时候,没m花了。

2.2.3 平均情况时间复杂度

数据在上述两种状况下的概率并不高,为表示平均情况下的时间复杂度,引入了平均时间复杂度的概念。

2.2.4 时间复杂度的系数、常数 、低阶

因为时间复杂度反映的是数据规模 n 很大的时候的一个增长趋势,所以它表示的时候会忽略系数、常数、低阶。
但是实际的软件开发中,我们排序的规模可能很小,在这种情况下,这些因素所造成的影响比较大,所以,在对同一阶时间复杂度的排序算法性能对比的时候,我们就要把系数、常数、低阶也考虑进来

2.2.5 比较次数和交换(或移动)次数

一般在基于比较的排序算法的执行过程,主要会涉及两种操作,一种是元素比较大小,另一种是元素交换或移动。所以,如果我们在分析排序算法的执行效率的时候,应该把比较次数和交换(或移动)次数也考虑进去,因为这些操作也会影响一定的性能。

因为时间复杂度这块并不是本篇文章的重点,可以移步百度自行深入了解。

2.2 排序算法的内存消耗

主要是指空间复杂度,对于同一个目标,不同的算法所占用的内存是不同的,占用的内存越多,空间复杂度越高,程序的性价比就越不高,原地排序算法,就是特指空间复杂度是 O(1) 的排序算法。

2.3 排序算法的稳定性

如果说待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变,那我们就说这排序算法是稳定的排序算法。
例如说,数组 [1,5,7,5],经过排序后得到的结果[1,5,5,7] , 第一个5和第二个5 的先后顺序还是不变,那就是稳定的。

三、常见的排序算法有哪些?

最常用的算法主要有:冒泡排序、插入排序、选择排序、归并排序、快速排序、计数排序、基数排序、桶排序
这几种算法的区别主要是

算法类型时间复杂度是否基于比较
冒泡、插入、排序O(n^2)
快排、归并O(nlogn)
桶、计数、基数O(n)

四、如何用 Go 实现排序算法

这里先实现 冒泡、插入、排序这三种比较简单的算法。(其实是其他的我还没写好)
话不多说。上代码。

4.1 冒泡排序

算法:每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换,一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作。
show code time

func bubblingSort(arr [8]int) [8]int {
	count := len(arr)
	for i := 0; i < len(arr); i++ {
		for j := 0; j < count-1; j++ {
			if arr[j] > arr[j+1] { // 判断大小决定是否替换
				temp := arr[j+1]
				arr[j+1] = arr[j]
				arr[j] = temp
			}
		}
		count-- //这是最关键的,每次处理后,最大的元素都会在最右边,因此后续要处理的数组长度均减一
	}
	return arr
}
4.2 插入排序

算法:插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束

func insertionSort(arr [8]int) [8]int {
	//[3, 6, 7], [5, 1, 2, 10, 4]
	//[3, 5, 6, 7],[1, 2, 10, 4]
	//[1,3, 5, 6, 7],[2, 10, 4]
	// 假设第一个元素已经是已排序区间的第一个值
	hasSortNum := 1             // arr[hasSortNum] 就是新的要处理的元素, hasSortNum 刚好是未排序区间的第一个元素下标
	for hasSortNum < len(arr) { // 说明都排完了
		// 遍历一下已排序区间,插入到合适的位置,并且搬运数据
		for k, _ := range arr[:hasSortNum] {
			// 找到要插入的位置
			if arr[k] > arr[hasSortNum] { // 此时 arr[k]就是要插入的位置
				// 搬运数据,需要将 k 位置给插入的元素,其他元素都后移,直到arr[hasSortNum]元素
				// k=2 hasSortNum=3 需要搬运2个位置
				// k=1 hasSortNum=4,需要搬运4个位置 (hasSortNum-k+1)
				// 先保留要被代替的元素
				temp := arr[hasSortNum]
				for i := hasSortNum; i > k; i-- { // k+1 到 hasSortNum 位置都要动
					arr[i] = arr[i-1]
				}
				arr[k] = temp
			}
		}
		hasSortNum++
	}
	return arr
}
4.3 选择排序

算法:选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。

func selectionSort(arr [8]int) [8]int {
	//[3, 6, 7, 5, 1, 2, 10, 4]
	//[1, 6, 7, 5 , 3, 2, 10, 4]
	hasSortNum := 0 //一开始已排区间为空
	for hasSortNum < len(arr) {
		minValue := arr[hasSortNum] // 取未排第一个元素作为比较值
		minValueIndex := hasSortNum
		for i := hasSortNum + 1; i < len(arr); i++ {
			// 遍历未排序区间,找出最小值
			if arr[i] < minValue {
				minValue = arr[i]
				minValueIndex = i
			}
		}
		// 替换两个的位置
		temp := arr[hasSortNum]
		arr[hasSortNum] = minValue
		arr[minValueIndex] = temp
		hasSortNum++
	}

	return arr
}

五、小结

本篇文章主要讲述了如何分析排序算法的好坏,以及几种常见排序算法(冒泡,插入、排序)的Go写法,排序算法是算法的基础,在日常的工作中,很多框架的工具包虽然已经封装过,但是了解它才能正确的使用它,这三种算法实际上在生产中比较少用,因为复杂度相对较高,后面我们再讲下性能更高的快排和归并排序。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MClink

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值