问题引入:你想输入一组数字(或学生成绩,或身高体重,或单纯一列数字),你要按从大到小或从小到大的顺序排列,用C语言怎么去实现它?
经典的排列方法有冒泡排序和选择排序以及希尔排序等等,这里我们用冒泡排序解决;
冒泡排序
你在这输入你要输入的数字,我们用输入代码:
int num=0;
printf("请问有几个数字你需要排序?\n");
scanf("%d",&num);
int a[num];
printf("请输入%d个字符:",num);
for(int t=0;t<num;t++)
{
scanf("%d",&a[t]);
}
方便举例子,我们假设输入6个数字,分别为89、45、60、56、90、34,这里我们使用升序排列进行计算!
那么首先我可以把数组的第一个数a[0],也就是89,和后面的数值进行比较,如果a[0]大于第二个数a[1],那我把a[0]里面的数和a[1]里面的数调换,。在这里,显然89大于45,所以第一轮调换后的序列应该是:45、89、60、56、90、34;
因为是升序排列,所以要使最大的数处在最后面的位置,所以我们还需要进行比较。继续,我们来进行第二轮,第二个数89和第三个数60比较,89大于60,继续交换,我们的目的是把最大的数排在最后,目前比较两轮为止最大的数是89,交换后的序列是:45、60、89、56、90、34;
继续观察:现在第三个数是89和第四个数56进行比较,89大于56,所以进行交换,交换完的序列是:45、60、56、89、90、34;
以此类推..............................直把把所有数字中最大的数放到最末位;
接下来开始找第二大的数字放到倒数第二位:所以我们这次遍历循环的次数是9次,减去前面已经排好的最大值(那个位置不用改变);
也就是说,遍历循环一次,找一个数放到后面的位置;
而每次遍历的数目不同,第一次遍历的数目是6,从6个数中找到最大值放到最末位(因为我们需要按照从小到大的顺序排列,所以把最大值放到最末尾);
for(i = 0;i < 6;i++)
{
if(a[i] > a[i+1])
{
int temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
}
}
第二次遍历循环的数目是5,不用管最后一位的数字(已经是最大值),也就是说是从5个数字中找到最大值放到倒数第二位;
for(i = 0;i < 5;i++)
{
if(a[i] > a[i+1])
{
int temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
}
}
第三次遍历循环的数目是4,不用管倒数第一位(最大值),倒数第二位(第二最大值),从4个数目中找到最大值放到倒数第三位;
for(i = 0;i < 4;i++)
{
if(a[i] > a[i+1])
{
int temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
}
}
............................
以此类推
第五趟遍历时,已经排列好末尾的四位,还剩下两个数值比较和排序
for(i = 0;i < 1;i++)
{
if(a[i] > a[i+1])
{
int temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
}
}
需要第六趟比较吗?不需要!已经排列好从后往前数的五位,剩下一位已经是最小的值,不需要去比较。
我们发现:遍历循环的次数和每次遍历循环的数目有关联
遍历循环的次数 | 每次遍历循环的数目 |
1 | 6 |
2 | 5 |
3 | 4 |
4 | 3 |
5 | 2 |
6 | 1 |
所以这里用到的技巧有“比较”和“交换”;
以上例子是10个数值,假如有n个数值需要去实现从大到小排列呢?
这里数值个数为n,i为比较的趟数,j是每一趟比较的数值个数;所以需要用到两个for循环:第一个for循环用来控制趟数,有n个数值,所以我们需要比较n-1趟(因为最后一个数值不需要比较,在数学中可以用10个数值之间有9个空这样的例子去比喻),第二个循环中,需要达到每一趟比较完之后,比较个数减1,可以用n-1-i去比较。
可以实现这一块的代码如下:
int i,j;
for(i = 0;i < n-1;i++)//控制是第几趟的比较
{
for(j = 0;j < n-1-i;j++)
{
if(a[j] > a[j+1])
{
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
冒泡排序整体代码如下:
int main()
{
int n;
printf("本代码将使用冒泡排序实现n个数值从小到大排列!\n");
printf("请问你要排列几个数?\n");
scanf("%d",&n);
int a[n];
int sum = 0;
int i,j;
printf("请输入%d个数值:",n);
for(i = 0;i < n;i++)
{
scanf("%d",&a[i]);
}
for(i = 0;i < n-1;i++)//鎺у埗鏄鍑犺稛鐨勬瘮杈?
{
for(j = 0;j < n-1-i;j++)
{
if(a[j] > a[j+1])
{
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
for(i = 0;i < n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
return 0;
}
选择排序
选择排序,即每次选择最大的值排在整体序列最后面,同样,这里遍历的次数也应该是n-1次,每一次遍历的数值个数也应该是递减的。所以接下来的步骤有:
1、先让这行序列中的第一个值与后面的值依次作比较,每遇到一个比它大的值,就记下这个值的下标(可以想到这里应该定义一个变量去保存下标);如果与后面的数比较时,后面的某个数不比它大,则不需要记录下标;
2、每遍历一次,第一个值与后面的值比较,找到一个最大值,将其下标记录下来;再通过最大值的下标找到最大值和最后一个数值替换;
第二次遍历,遍历剩下的n-1个数,将第一个数替换倒数第二个数值
...........................
以此类推
注意:1、如果最大值下标不和末尾的对应位相等,则进行替换,如果相等,则不替换; 2、这里需要注意,最大值下标不变;最大值下标里面的值改变了;第二次遍历的时候继续从第一个数与最大值下标更新的值那里作比较;
选择排序整体代码实现如下:
int main()
{
int num;
printf("本代码将使用选择排序实现n个数值从小到大排列!\n");
printf("请输入你要排几个数字:\n");
scanf("%d",&num);
int a[num];
printf("请输入你要排序的数字:\n");
for(int i=0;i<num;i++)
{
scanf("%d",&a[i]);
}
int t=0;//下标
int max_t=0;//最大值下标
for(int j=0;j<num-1;j++)
{
for(int k=0;k<num-j-1;k++)//每次遍历的数目随着遍历次数减少
{
if(a[max_t]<a[k])
{
max_t=k;
}
}
//如果最大值下标不和末尾的对应位相等,则进行替换,如果相等,则不替换
if(max_t!=num-j-1)
{
int temp=a[max_t];
a[max_t]=a[num-j-1];
a[num-j-1]=temp;
}
}
printf("从小到大排序为: ");
for(int o=0;o<num;o++)
{
printf("%d\t",a[o]);
}
return 0;
}
冒泡排序和选择排序的比较
冒泡排序: 两两比较,互相交换,假如有N个元素,在一趟冒泡排序中,最坏的情况,交换 n-1 次,最好的情况,交换0次;
选择排序:先在整个序列中找最大值,再和最后的位置进行对调;然后除去最大的(最末位)值之外,第二趟遍历剩下的值,后面再找第二大值;假如有N个元素,最坏的情况也是交换N-1次;
总的来说,在我看来,这都是很巧妙的方法,在今后的学习中,要学习这种思维模式,努力学习C语言,尝试更跳脱的思考,不再给自己画个圈子、固步自封!
这是我在学C语言中,通过老师讲解和询问同学对自己的学习的一个总结,文中有不对或说法不准确的地方,恳请各位大佬指正!
本人的评论区欢迎大家讨论技术性的问题!