题目:
输入n个数字,升/降序排列输出(以升序为例)
输入示例:8 2 5 6 3
输出示例:2 3 5 6 8
本题目是上一篇文章(《求n个数的最大数(C语言)》)的进阶:求n个数的最大数(C语言)
思路:冒泡排序
本质是将上一篇文章(《求n个数的最大数(C语言)》)的思路作为一个循环单位循环进行
详细思路:
第一次循环
- 将第1个数字与第2个数字比较,若大于,二者数值交换,否则不变
- 将第2个数字与第3个数字比较,若大于,二者数值交换,否则不变
- ...
- 将第n-1个数字与第n个数字比较,若大于,二者数值交换,否则不变
第二次循环
- 将第1个数字与第2个数字比较,若大于,二者数值交换,否则不变
- 将第2个数字与第3个数字比较,若大于,二者数值交换,否则不变
- ...
- 将第n-2个数字与第n-1个数字比较,若大于,二者数值交换,否则不变
...
第n-1次循环
- 将第1个数字与第2个数字比较,若大于,二者数值交换,否则不变
排序结束 输出
难点一:冒泡排序的实现
错误示例:
for (i=1;i<n;i++)
{
if (a[i]<a[i-1])
t=a[i];
a[i]=a[i-1];
a[i-1]=t;
}
正确示例:
for(i = 0; i < n - 1; i++)
{
for(j = 0; j < n - i - 1; j++)
{
if(a[j] > a[j + 1])
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
二者区别:前者只进行了一次遍历,相当于只做了思路中的第一个循环(这样的效果仅仅是把最大的元素调换到最后一个位置),而后者便是双层循环,将思路中的所有内容完全贯彻执行了
难点二:一维数组的输出
错误示例:
printf("the ranged elements are :%d",a[i]);
正确示例:
printf("the sorted elements are: ");
for(i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
- 后者 语法规范,使用循环输出
- 后者的英语表达更合适(sorted)
难点三:for循环中的第二个条件:变量范围的把握 [引用部分代码是完整代码(底部提供)中的内循环部分]
错误示范:
for(j = 0; j < n - i ; j++)
正确示范:
for(j = 0; j < n - i - 1; j++)
1. 条件 j < n - i
- 循环次数:内层循环会执行
n - i
次(从j=0
到j=n-i-1
)。 - 适用场景:
- 当算法需要遍历当前未处理的全部元素时(例如:每轮处理的范围不收缩,或需要覆盖所有剩余元素)。
- 可能用于需要重复访问所有元素的逻辑(如某些动态规划或未优化的简单算法)。
- 风险:
- 如果循环内部涉及对
j+1
的引用(例如比较相邻元素),可能会导致数组越界(访问arr[j+1]
时j
最大为n-i-1
,而j+1
可能达到n-i
,超出数组范围)。
- 如果循环内部涉及对
2. 条件 j < n - i - 1
- 循环次数:内层循环会执行
n - i - 1
次(从j=0
到j=n-i-2
)。 - 适用场景:
- 当算法需要避免访问越界元素时(例如:比较
arr[j]
和arr[j+1]
)。 - 冒泡排序的经典实现:每轮外层循环
i
会将一个最大元素“冒泡”到末尾,因此内层循环无需再处理已排序的部分。 - 适用于需要严格边界控制的场景。
- 当算法需要避免访问越界元素时(例如:比较
混淆一:数组越界
错误示例:
for(i = 0; i <= n -1; i++) //错误在这一行 //有的人也错写为i<n
{
for(j = 0; j < n - i - 1; j++)
{
if(a[j] > a[j + 1])
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
正确示例:(同难点一)
for(i = 0; i < n - 1; i++)
{
for(j = 0; j < n - i - 1; j++)
{
if(a[j] > a[j + 1])
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
错误:数组越界(详见《求n个数的最大数(C语言)》难点三)(链接在顶部)
混淆二:升序排序和降序排序的区别
升序:
if(a[j] > a[j + 1])
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
降序:
if(a[j] < a[j + 1])
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
总结
完整代码:
#include <stdio.h>
int main()
{
int n, i, j, t;
printf("please input the number of the elements: ");
scanf("%d", &n);
// 动态分配内存
int a[n];
printf("please input the elements: ");
for(i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
// 使用冒泡排序对数组进行排序
for(i = 0; i < n - 1; i++)
{
for(j = 0; j < n - i - 1; j++)
{
if(a[j] > a[j + 1])
{ // 这里是升序排序,如果需要降序排序,改为 a[j] < a[j + 1]
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
printf("the sorted elements are: ");
for(i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
输出效果:
补充
使用scanf别忘加&
把所有要定义的变量统一定义在前面的优缺点:
优点
-
清晰性:所有变量在函数开头定义,可以一目了然地看到所有变量。
-
兼容性:与旧的 C 标准兼容。
缺点
-
可读性:变量定义与使用相距较远,可能会降低代码的可读性。
-
作用域:变量的作用域更大,可能会导致意外的错误。
现代实践
现代 C 编程更倾向于在需要时就近定义变量,这样可以提高代码的可读性和维护性。