[C语言] 部分经典排序算法详解(有图解)
目录
1.内容概括
2.主要算法
3.技术的具体应用
4.算法实际应用
5.总结
0.前言
- 在上一篇文章《[C语言] 数组的实际应用三则》中我们提到了数组的一些基础知识,并通过三个实际例子来加深对数组的理解
- 本文则重点讲解数组与排序算法的不解之缘
1.内容概括
-
完善《[C语言] 数组的实际应用三则》中的算法
-
讲解数组的几种常见的排序方法
2.主要算法
本文中主要讲解三种排序方法:冒泡排序、选择排序、插入排序
我们先假设存在一个数组a[4],其元素分别为a2,a4,a3,a1
其中a1<a2<a3<a4
2.1冒泡排序
算法思路图解:

算法文字描述:
①从第一个元素开始,将第一个元素与下一个元素进行比较,如果前一个元素比下一个元素更大,则互换这而位置
②然后开始比较第二个和第三个的大小,判断是否需要互换,然后是第三个与第四个
③重复上述操作可以将最大(当然也可以是最小)的数字放在数组最后
④然后逐步将第二大,第三大的数字依次沉底
⑤反复上述过程,直至排序完成
2.2选择排序
算法思路图解:

算法文字描述:
①从第一个元素开始,将第一个元素赋值给min,然后跟后面的元素一次比较
②如果min比后面的某个元素大,则互换第一个元素与比min小的这个元素,然后将把比原来的min小的元素赋值给min
③重复上述操作不断向后比较,直至比对完最后一个元素
④当比对完最后一个元素后就会发现第一位的元素已经被替换为数组中最小的那个元素
⑤下一次循环从第二位开始,循环结束后,第二位则为第二小的元素
⑥重复上述过程即可
2.3插入排序
算法思路图解:

算法文字描述:
①从第一个元素开始,将第一个元素与下一个元素相比较
②如果第一个元素大于第二个,则互换二者位置
③当发生互换位置后,关注那个被往前(往下标较小的方向)调的元素,将这个元素与前一个元素相比(如果有前一个元素的话)
④如果这个往前调的元素比前一个元素更小,则继续往前换
⑤重复④过程,直至前一个元素更小
⑥视线回到①中往后调的那个元素,重新向后比较
⑦重复上述过程即可
3.主要算法具体实现
3.1冒泡排序
for (int j = 3; j > 0; j--) {
for (int i = 0; i < j; i++) {
if (p[i] > p[i + 1])
{
temp = p[i];
p[i] = p[i + 1];
p[i + 1] = temp;
}
}
}
或者也可以是
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3 - i; j++) {
if (p[i] > p[i + 1])
{
temp = p[i];
p[i] = p[i + 1];
p[i + 1] = temp;
}
}
}
算法特点:
- 整体比较简单易懂,使用两个for循环嵌套
- 两层for循环分别负责不同的部分,具体情况视写法而定
- 由于要互换两个变量的值,一般需要一个中间变量(当然如果是变量存的是数字,也可以靠数学方法解决)
a = a+b;
b = a-b;
a = a-b;
这样也能互换二者的值
3.2选择排序
for (int i = 0; i < 3; i++) {
min = p[i];
k = i;//k代表取出来的那个数字的位置
int j = i + 1;
for (; j < 4; j++) {
if (min > p[j]) {
p[k] = p[j];//p[j]表示正在用阿里对比的那个
p[j] = min;//相当于调换位置
min = p[k];//min得换为新的最小的
}
}
}
算法特点:
其实每次要做的事情就是比较二者大小并判断是否需要互换,只不过该算法中专门设置了一个变量min,用min去进行比较
每次循环结束,都能获得一个次小值
if语句中确实地将数组中两个元素互换,然后改变了min的值,事实上这样仅为了方便理解,其实没有必要
其实只需要将min的值改变,然后在将原来min的值(数组里还放着没动)赋给比min小的那个元素的位置即可
3.3插入排序
int temp = 0;
int total = 3;
for (int i = 0; i < total; i++) {
if (p[i] > p[i + 1]) {
//后面的更小则换位置
temp = p[i];
p[i] = p[i + 1];
p[i + 1] = temp;
int k = i;
bool flag = true;//用来判断需不需要继续往前换
//换完位置后继续判断
while (k > 0 && flag ) {
if (p[k] < p[k - 1]) {
temp = p[k];
p[k] = p[k -1];
p[k - 1] = temp;
k--;
}
else {
//不用继续往前换,退出循环
flag = false;
}
}
}
}
算法特点:
其实插入排序的思路和冒泡排序很相似,只不过每次互换后,往前调的元素还需要继续往前比较
简而言之,就是确保往前调的元素能直接找到正确的位置
4.算法实际应用
这里的应用例子就是《数组的实际应用三则》中的猜数字游戏
上文中提到的该游戏的正确答案是乱序,而为了降低难度,我们现在将其改为顺序排列
改为顺序排列的方式就是使用上文提到的三种排序方法
int num = 0;
for (int i = 0; i < 4; i++) {
num = rand() % 10;
//printf("%d", num);
//保证四个数字不重复
for (int j = 0; j < i; ) {
if (num == p[j]) {
num = rand() % 10;
j = 0;
}
else {
j++;
}
}
p[i] = num;
}
//上面为产生4个随机数,依次放入数组中,下面为是否需要排序
int temp = 0;
printf("请选择难度:(低难度1,高难度2)");
scanf_s("%d", &temp);
//如果选择低难度,则采用冒泡排序按顺序排好
if (temp == 1) {
//BubbleSort(p);//冒泡排序
SelectionSort(p);//选择排序
//InsertioSort(p);//插入排序
}
- 这里的p实际是一个指针,如果不是很懂指针的同学,可以暂时将这个p当做一个数组的名字即可
- 实际程序运行的时候,就可以通过输入1或2,来判断是否运行排序程序
另外,除了将数字放入数组成功后再进行排序,还可以在插入的时候就进行排序:
for (int i = 0; i < 4; i++) {
num = rand() % 10;
numArray[i] = num;
if (i == 0) {
//如果是第一个,不比较直接放入
a[i] = num;
total++;
}
else {
//如果不是第一个,得开始比较
int site = total - 1;//从最后一个开始往前比较(total为总数,减1为最后一位)
while (site >= 0) {
if (a[site] > num) {
//说明当前的最后一个元素必然得往后移
a[site + 1] = a[site];
}
else {
//a[site] <= num
//放在这个元素的后面
a[site + 1] = num;
total++;
break;
}
if (site == 0) {
//如果site指向第一个元素,则没法再比较了
a[site] = num;
total++;
break;
}
site--;
}
}
}
主要代码思想:
①第一个元素直接放入元素第一个位置
②从第二个元素开始,每个元素插入前都要从后往前,与数组已经存在的元素逐个进行大小比较
③如果待插入元素小于已存在元素,已经存在元素就要向后挪(就是赋值到后面一个的内存空间)
④直至遇到一个小于待插入元素的元素,则待插入元素放在该元素后面
⑤重复②-④步骤,直至插入完成
注意:
-
强调从后往前比较的原因在于,从后往前,要做的是将待插入元素和已存在元素依次比较,如果需要向后挪动的话,就立即挪动
-
如果从前往后比较,其实道理也一样,但问题在于,找出可以插入的位置后,需要将后面所有的已存在元素挪动,为了完成这个动作需要专门写一个较为复杂的f循环,总体结构不一定好看
-
当然以上结论为鄙人的个人看法,事实未必一定如此,读者有兴趣的话,可以自行进行尝试和比较
5.总结
(1)本文主要解读了三种(加上最后一种实际上有四种)与数组有关的排序方法,由于动图不容易制作,因此选择图解+文字说明的方式,读者应该也能比较清晰地了解相应的排序方法的理论过程
(2)将排序算法转化的这个过程,其实更重要的是理解排序算法是如何运作的,然后就是根据自己对于算法、代码的理解进行编写即可,不需要死记硬背(死记硬背毫无用处)
(3)下一篇文章应该会开始讲解指针的相关内容,指针与数组也有说不清道不明的关系,本文也算是为之后的内容铺平道路而做的准备吧