输入n,再输入n个数,对n个数进行排序(本文以升序为例,降序反之)

1、冒泡排序

​思想:从第一个数开始,利用循环,比较前一个数a[i]和后一个数a[i+1]的大小,如果a[i]>a[i+1],则交换a[i]a[i+1]的值。最后,使得每次循环的最大值都被交换到了该数列的最后,所以每次循环后最后一个数都是排好了的,即每次循环后还剩余未排序的数n-1,代码如下:

#include <stdio.h>       //c语言头文件
int main() //主函数
{
int n, a[10000],i,j,max; //定义类型
scanf("%d", &n); //输入n,表示接下来需要输入n个数
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]); //利用for循环,输入n个数,存入数组a[i]
}
max = a[0]; //首先将数组第一个数a[0]赋给max

for (i = 0; i < n; i++) //第一层循环,表示要排n个数,每个数要排一次
{
for (j = 0; j < n - i - 1; j++)//第二层循环,表示每排一个数,并且要比较(n-i-1)次
{ //因为每排完一个数,这个数下一轮就不用排了,还剩下未排完的数的数量就不断-1,再深思一下,很好想的
if (a[j] > a[j+1]) //比较前后两个数的大小 ,如果前一个数大于后面一个数,则进入if交换值
{
max = a[j+1]; //将后一个值赋给一个变量,进行一个值的存储
a[j+1] = a[j]; //将前一个值赋给后一个值
a[j ] = max; //将存储后一个值的变量赋给前一个值,这样就完成交换了
}
}
} //进行到这里就交换完成了
for (i = 0; i < n; i++) {
printf("%d ",a[i]); //最后利用for循环输出数组a[i]
}
return 0;
}

​为了让和笔者一样的小白能看懂,笔者注释了很多

其实,上面的代码还不够完美。设想:如果循环进行到某一次的时候,该数列已经排好,但是,上面的代码还是会继续执行下去,这就做了很多无用功,程序跑的慢,用户体验差,所以接下来我们做一个代码上的优化。

冒泡排序优化(一)

#include <stdio.h>
int main()
{
int n, a[10000],i,j,max;
scanf("%d", &n);
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
max = a[0];
for (i = 0; i < n; i++)
{
int flag = 0; //立一个flag=0
for (j = 0; j < n - i - 1; j++)
{
if (a[j] > a[j+1]) //如果程序还没排好,则会进入if
{
max = a[j+1];
a[j+1] = a[j];
a[j ] = max;
flag = 1; //进入if后flag=1
}
}
if (flag == 0) //在第二层循环结束后,如果flag还是等于0,则说明没有进入if,即程序已经排好,这时候就可以退出排序了
break; //这样就节省了不必要的时间
}
for (i = 0; i < n; i++) {
printf("%d ",a[i]);
}
return 0;
}

有些数列,后面很多是有序的,而前面是无序的,这时候我们就不需要再将后面的数据再进行排序,所以,只要我们循环每走一遍并记下该次最后交换值的下标,再轮到下一个循环时,就只进行到上一个被记下的下标为止就好了。这也为程序大大节省了空间。

冒泡排序优化(二)

​优化如下:
#include<stdio.h>                     //冒泡排序优化二
int main()
{
int n, a[10000], i, j,temp,index_1,index_2;
scanf("%d", &n); //输入n
for (i = 0; i < n; i++) { //利用for循环输入n个数存入数组a[i]
scanf("%d", &a[i]);
}

index_2 = n; //(很关键的一步)1、让index他两都初始化为n
index_1 = index_2; //2、下面存储下标的值赋给index_1,
for (i = 0; i < index_1; i++) //第一层循环
{
for (j = 0; j < index_1-1; j++) //第二层循环(同冒泡排序)
{
if (a[j] > a[j + 1]) //判断前后两个值的大小
{
temp = a[j + 1];
a[j + 1] = a[j];
a[j] = temp; //到这里与冒泡排序都是一样的,接下来就是区别了(注意)
index_2 = j; //每次交换后,将前一个值的下标(j)进行存储给到index_2
}
}
index_1 = index_2+1; //将存储下标的值加1赋给index_1,使它重新开启一个新的循环
i = 0; //开启一个新的循环就要把i重新定为零,程序执行的最后会因为i=1<index_1不成立而结束程序(此时index_1应该是为1)
}//到这里就排完了
for (i = 0; i < n; i++) {
printf("%d ", a[i]); //利用for输出数组
}
return 0;
}
优化一与优化二结合版(其实这里的flag可加可不加,意义不是很大,因为每次循环后index_1的值都会更新一次,不交换其就为1,i=1<index_1不成立就结束循环了,所以这个结合版就随便了解一下,不用太过在意)
#include<stdio.h>                     //冒泡排序优化二
int main()
{
int n, a[10000], i, j, temp, index_1, index_2;
scanf("%d", &n); //输入n
for (i = 0; i < n; i++) { //利用for循环输入n个数存入数组a[i]
scanf("%d", &a[i]);
}

index_2 = n; //(很关键的一步)1、让index他两都初始化为n
index_1 = index_2; //2、下面存储下标的值赋给index_1,
for (i = 0; i < index_1; i++) //第一层循环
{
int flag = 0; //立一个flag=0

for (j = 0; j < index_1 - 1; j++) //第二层循环(同冒泡排序)
{
if (a[j] > a[j + 1]) //判断前后两个值的大小
{
temp = a[j + 1];
a[j + 1] = a[j];
a[j] = temp; //到这里与冒泡排序都是一样的,接下来就是区别了(注意)
index_2 = j; //每次交换后,将前一个值的下标(j)进行存储给到index_2
flag = 1; //进入if 则flag=1
}
}
index_1 = index_2 + 1; //将存储下标的值加1赋给index_1,使它重新开启一个新的循环
i = 0; //开启一个新的循环就要把i重新定为零,程序执行的最后会因为i=1<index_1不成立而结束程序(此时index_1应该是为1)
if (flag == 0) //同优化一,若flag还是等于0.则没有进入if,就说明已经排完序了
break;
}//到这里就排完了
for (i = 0; i < n; i++) {
printf("%d ", a[i]); //利用for输出数组
}
return 0;
}

冒泡排序优化(三​)

​思想:还有一种方法可以节省时间,如果每次排序,我弄两次,把最大值弄最后,把最小值弄最前,就节省了一半重复循环的次数,程序的运行速度就可以加快、(前面两种其实已经可以了)

#include<stdio.h>   //冒泡排序优化3
//思想;每轮循环排两次序,将最大值排到最后,将最小值排到最前,这样就节省了很大的空间
int main()
{
int n,a[10000];
int i, j;
int x, y;
int min, max;
scanf("%d", &n); //输入n
for (i = 0; i < n; i++) { //利用for循环输入n个数存入数组
scanf("%d", &a[i]);
}
min = a[0];
max = a[0]; //初始化

for (x = 0; x < n/2; x++) //第一层循环
{
for (y = x; y < n-x-1 ; y++) //第二层(max)循环
{
if (a[y] > a[y + 1]) //从头到尾,将该次循环的最大值排到最后
{
max = a[y + 1]; //交换3步骤
a[y + 1] = a[y];
a[y] = max;
}
}
//for (i = n - 1- (x + 1);i>0; i < n - 2 * x - 2; i--;)
for(i=n-1-(x+1); i>x ;i--) //第二层(min)循环
{
if (a[i] < a[i - 1]) //从尾到头,将该次循环的最小值排到最前
{
min = a[i - 1]; //交换3步骤
a[i - 1] = a[i];
a[i] = min;
}
}
}

for (i = 0; i<n ; i++) { //排完后,利用for循环将数组输出
printf("%d ", a[i]);
}
return 0;
}

下面这个是优化三立了flag的,立一个flag是一个好习惯,避免程序重复执行已完成的程序。

#include<stdio.h>   //冒泡排序优化3
//思想;每轮循环排两次序,将最大值排到最后,将最小值排到最前,这样就节省了很大的空间
int main()
{
int n,a[10000];
int i, j;
int x, y;
int min, max;
scanf("%d", &n); //输入n
for (i = 0; i < n; i++) { //利用for循环输入n个数存入数组
scanf("%d", &a[i]);
}
min = a[0];
max = a[0]; //初始化

for (x = 0; x < n/2; x++) //第一层循环
{
int flag = 0;
for (y = x; y < n-x-1 ; y++) //第二层(max)循环
{
if (a[y] > a[y + 1]) //从头到尾,将该次循环的最大值排到最后
{
max = a[y + 1]; //交换3步骤
a[y + 1] = a[y];
a[y] = max;
flag = 1;
}
}
//for (i = n - 1- (x + 1);i>0; i < n - 2 * x - 2; i--;)
for(i=n-1-(x+1); i>x ;i--) //第二层(min)循环
{
if (a[i] < a[i - 1]) //从尾到头,将该次循环的最小值排到最前
{
min = a[i - 1]; //交换3步骤
a[i - 1] = a[i];
a[i] = min;
flag = 1;
}
}
if (flag == 0)
break;
}

for (i = 0; i<n ; i++) { //排完后,利用for循环将数组输出
printf("%d ", a[i]);
}
return 0;
}

2、选择排序

​思想:每次循环记下最小值的下标,循环后将最小值赋给数列前面还未排序的第一个值

#include<stdio.h>                 //选择排序详解
//思想:每次循环扫描出该循环的min,与数组前的值进行交换
int main()
{
int n, a[10000], i, j, index, temp, min;
scanf("%d", &n); //输入n
for (i = 0; i < n; i++) { //利用for循环输入n个数存入数组
scanf("%d", &a[i]);
}
for (i = 0; i < n; i++) //第一层循环
{
index = i; //将i值赋给index做初始化,i值会一直递增,是因为每进行一次循环,前面就排好了一个值
//重点: i值就是前面未排好的数的第一个下标,所以用i做index的初始化
//到这里还蒙的话不要着急,继续往下看
min = a[i]; //利用数组第一个值给min初始化
for (j = i + 1; j < n; j++) //第二层循环
{
if (min > a[j]) //比较min与数组元素的大小
{
min = a[j]; //将比min还小的数组值赋给min
index = j; //并将下标(j)赋给index,记录该次循环min的下标
}
}
if (index != i) // 判断index的值是否等于还未排好的第一个值的下标
{ //不等于则进行交换三步骤
temp = a[i]; //将为排好的第一个值赋给变量temp进行存储
a[i] = a[index]; //将做记号的最小值a[index]赋给a[i],此时a[i]以及之前的数都排好了
a[index] = temp; //将存储a[i]原先值的temp赋给a[index]
}
}//结束了
for (i = 0; i < n; i++) {
printf("%d ", a[i]); //最后利用for循环输出数组a[i]
}
return 0;
}

总结语:我其实是一个小白,我一直想用朴素的语言写出能让和我同样的小白都能看懂的文章,为此我会加倍努力的,希望可以得到大家的支持,我也会不断提高自己,分享更多的知识(我懂的)给到大家,谢谢啦!我们下期再见!(我叫康少)