以下是本节涉及的知识点:
- 冒泡排序法
- 交换数组元素
- 二重循环
- 其他常用排序法
本节是贸大95%学生的噩梦。其实我觉得,之所以是噩梦,可能只是老师没讲清楚,自己上课没认真听,ppt也没说清楚导致的。所以我在这里会尽可能地简单地把冒泡排序法拆解成一个一个的小步骤。希望各位能尽可能地理解。
当然实在理解不了也没问题。本来排序法就不是考试的重点,只有一个比较小的概率会出成程序大题。但是如果理解了的话,会对自己编程能力有很大提升。
首先还是说说排序法为什么很重要。
- 排序是很常见的操作
我们在很多情况下都需要对数据进行排序,比如考试成绩出来了,我们想看自己排在第几名,亦或是玩牌的时候对自己抽到的牌按自己希望的顺序排列,都需要用到排序。 - 排序是很复杂的操作
把东西按照自己希望的顺序去排列,即使在现实中也是一个相当大的工程量(请自行想象把一个考室所有学生的卷子打乱,再让你按学号从小到大顺序排列)图书管理员为图书辛辛苦苦编号,分门别类排列,就只为减少排序和查找的工作量。
排序的方法有很多种,我们现阶段可能接触得到有冒泡排序法、选择排序法、插入排序法和快速排序法。它们都是以排序方法的基本思路命名的。就我个人而言,我在现实中对什么文件进行排序的话,我都是用的插入排序法(下面会介绍其基本思路)。
文章目录
冒泡排序法基本原理
冒泡排序法是排序法里面最低级,最低效,但也是最简单的一种排序法,因为其对元素的排序过程像是水中的气泡在上浮,所以被称为冒泡排序法。
其基本思路是这样的:逐个比较相邻元素,如果有顺序不对的,就把它们调换,使得顺序正确。只走访一次数组,基本上不能完成排序。但只要我们不断重复走访数组,不断地找到顺序有误的相邻元素,不断地纠正其顺序,就可以实现让数组按顺序排列。
比如下面是一个例子,我们试图把下面的数组按从小到大顺序排列:
5 1 4 2 3
对于这个数组,我们首先比较其头两个元素,发现5
和1
的顺序反了,1应该在5的前面,所以我们把5
和1
调换位置,得到
1 5 4 2 3
接下来我们比较第2个和第3个元素,发现5
和4
的顺序依旧反了,所以我们让5
和4
调换位置,得到
1 4 5 2 3
接下来是第3个和第4个元素,发现5
和2
的顺序依旧反了(事实上,直到5
被放到最后之前,都一直是5
和其他数比较),所以我们让5
和2
调换位置,得到
1 4 2 5 3
接下来是第4个和第5个元素,发现5
和3
的顺序依旧反了,所以我们让5
和3
调换位置,得到
1 4 2 3 5
到这里,我们已经成功走访了一次数组,并实现了4次调换。会发现此时的数组已经开始稍微有点顺序了。
此时在数学上可以证明:数组中最大的元素已经被我们移到数组末尾,不论它原先在什么位置。
但数组还没有实现完全的按顺序排列。所以我们重新开始比较头两个元素:
1 4 2 3 5
其头两个元素,1
和4
的顺序是正确的,所以我们不调换其位置。下面比较第2和第3个元素:
1 4 2 3 5
我们发现4
和2
的顺序反了,所以我们把4
和2
交换顺序,得到:
1 2 4 3 5
下面比较第3和第4个元素,发现4
和3
的顺序反了,所以我们把4
和3
交换顺序,得到:
1 2 3 4 5
因为前一次走访中,我们已经让最大的元素移到了第5号位置,所以此时无需再比较第4和第5个元素,因为第5个元素必然是最大的。
由于这个操作和前一次走访数组是一样的,所以同理也有这个性质:数组中第二大的元素已经被我们移到了倒数第二的位置,不论它原先在什么位置。
当然此时已经把数组排好序了,但如果顺序依旧不对的话,也是这样做。让第三大的元素排到倒数第3号位,直至数组实现完全从小到大顺序排列。
至此,数组排序完毕。
我们可以发现,元素5
在第一次走访数组的时候,从数组的第一号位置,逐渐移到最后一号位置,其行为就像是一个石头逐渐下沉(或者一个气泡逐渐上浮),这就是冒泡排序法的名字由来。
用数学化的语言来说,冒泡排序法就是这样的:
-
对于一个长度为
N
的数组:- 对于其第
1
到第N-1
个元素,我们逐一比较这个元素与其之后的元素的大小顺序,若相反,则交换他们次序,使得这两个元素顺序正确。 - 走访第一遍以后,我们可以确保最大的元素已经在最后的位置了。
- 对于其第
-
然后我们就只需要对数组的前面
1
到N-1
个元素进行排列:- 排列的方法是一样的,不过现在我们走访只需要走访其第
1
到第N-2
个元素了。 - 当此次走访完后,我们可以确保原数组里面倒数第二大的元素已经在倒数第二个位置。也就是说数组的第
N-1
和第N
个元素已经实现了有序排列。
- 排列的方法是一样的,不过现在我们走访只需要走访其第
-
接下来是对数组的前面
1
到N-2
个元素进行排列:- 排列的方法是一样的,不过现在我们走访只需要走访其第
1
到第N-3
个元素了。 - 当此次走访完后,我们可以确保原数组里面倒数第三大的元素已经在倒数第三个位置。也就是说数组的第
N-2
到第N
个元素已经实现了有序排列。
- 排列的方法是一样的,不过现在我们走访只需要走访其第
-
接下来就不断重复这个操作,需要排序的部分越来越短,后面已经排好序的部分越来越长。
-
直到需要排序的部分长度为1时,此时就不需要再进行任何排序。
-
此时这个数组在数学上就已经可以被证明是有序排列的了。
冒泡排序法的代码实现
首先,还是从一个情景展开讨论:
针对一个长度为N的整数数组(假定N=15),其中可能有重复的数字,也可能没有。请用冒泡排序法使其内部元素按从小到大顺序排列。
根据前面我们对冒泡排序法的讲解,我们可以把这个情景分解为如下步骤:
- 定义并初始化数组
- 判断某个元素与它后面的那个元素的大小关系,如果顺序反了,就交换它们的位置
- (第
1
次走访数组)对于前N-1
个元素的每一个都执行一遍上述操作
执行完前一操作后,(第2
次走访数组)再对数组前N-2
个元素的每一个执行一遍第二步的操作
执行完前一操作后,(第3
次走访数组)再对数组前N-3
个元素的每一个执行一遍第二步的操作
……
直到第N-2
次走访数组,此时只需要对数组的前1
个元素执行第二步的操作(对第1
个和第2
个元素调整顺序)。此时无需再次走访数组,数组已经达到有序状态。
总结便是这样: - 对于第
i
次走访数组,我们都对数组的前N-i
个元素的每一个执行一遍第二步的操作。
其中i
从1
一直加到N-2
。 - 当上述操作结束后,数组变得有序。此时只需在屏幕上输出数组
定义并初始化数组
首先我们先把最简单的工作做了:定义并初始化数组:
#include<iostream>
using namespace std;
int main(){
const int N = 15;
int a[N]={
9,7,15,6,2,18,6,24,-3,12,7,15,1,0,4};
}
其中数字是我随便输的,可以发现其中有重复的数字。不过这并不影响我们排序。
交换变量的值
接下来我们来做最基础的工作:交换两个变量的值。我们一般情况下是需要引入一个新的变量来暂时存储其中一个,不然就很难进行交换。(可以理解为,想要把两个箱子的位置交换,必须先把一个箱子移到别的地方,再把另一个箱子挪到这个箱子位置,最后再把最开始那个箱子挪到正确的位置去。)
交换的代码可以是这样:
int tmp;
tmp=a[j];
a[j]=a[j+1];
a[j+