完整一轮没有交换,
就可以提前结束
今天在昨天的冒泡排序上增加交换标记和两个计数器,观察已经有序、近乎有序与反向排列的数组需要多少次比较和交换。
当前一对没交换,不等于整个数组有序
对于 {1,3,2,4},第一对 1 和 3 不需要交换,但下一对 3 和 2 需要交换。所以必须从左到右完成一整轮比较,才能判断这一轮是否真的没有交换。
用 swapped 记录本轮是否交换过
每轮开始时把 swapped 设为 0;只要真正交换过一次,就设为 1,并在本轮剩余时间保持 1。它记录的是“本轮是否曾经交换”,不是“刚才这一对是否交换”。
一开始,我把同一轮的第二次比较误当成第二轮,认为不交换后应该把标记改回 0。逐步追踪后,确认只有下一轮开始时才重置,后面的不交换不能抹掉本轮已经发生的交换。
在内层循环结束后判断
完整内层循环结束后,如果 swapped == 0,就执行 break。这里的 break 位于内层循环外、外层循环内,结束的是外层排序循环;之后仍会输出数组和统计结果。
对于 {2,1,3,4},第一轮交换后数组已经有序,但标记为 1,因此还会进入第二轮;第二轮没有交换,才通过标记确认可以提前结束。
区分本轮标记与全程计数
- swapped:每轮重置,只回答本轮是否交换过。
- compare_count:在外层循环前初始化,每比较一对相邻元素就加 1,不交换也要计数。
- swap_count:在外层循环前初始化,仅在交换分支里加 1,累计整个排序过程的交换次数。
比较次数只统计数组元素之间的大小比较,不包含 for 的继续条件判断。三条赋值共同完成一对元素的交换,因此算一次交换。
最终练习代码
#include <stdio.h>
int main(void)
{
int values[] = { 4, 3, 2, 1 };
size_t count = sizeof(values) / sizeof(values[0]);
size_t compare_count = 0;
size_t swap_count = 0;
for (size_t round = 0; round < count - 1; round++)
{
int swapped = 0;
for (size_t index = 0; index < count - 1 - round; index++)
{
compare_count++;
if (values[index] > values[index + 1])
{
int temp = values[index];
values[index] = values[index + 1];
values[index + 1] = temp;
swapped = 1;
swap_count++;
}
}
printf("第 %zu 轮结束,swapped = %d\n", round + 1, swapped);
if (swapped == 0)
{
printf("已经有序,提前结束\n");
break;
}
}
printf("排序后的数组:\n");
for (size_t index = 0; index < count; index++)
{
printf("%d ", values[index]);
}
printf("\n");
printf("比较次数:%zu\n", compare_count);
printf("交换次数:%zu\n", swap_count);
return 0;
}三组实际运行验证
初始数组 比较次数 交换次数
{2,1,3,4} 5 1
{1,2,3,4} 3 0
{4,3,2,1} 6 6
三组最终数组:1 2 3 4{2,1,3,4} 的两轮标记依次为 1、0,第二轮后提前结束;{1,2,3,4} 第一轮就确认有序,只比较 3 次。昨天没有提前结束的版本对四个元素固定比较 3+2+1=6 次。
{4,3,2,1} 每轮都发生交换,三轮标记均为 1;数组依次变为 {3,2,1,4}、{2,1,3,4}、{1,2,3,4}。它不会显示提前结束提示,而是完成规定轮数后自然结束。三组运行均正常退出,代码为 0。
订正与最后的理解检查
初次统计 {2,1,3,4} 时,我把每轮比较算成了一次。逐对列出后,确认第一轮比较 3 次,第二轮比较 2 次,总共 5 次;即使没有交换,也已经进行了一次比较。
反向排列题中,三轮数组和每轮次数都预测正确,只是在问总数时漏看了“总共”,回答了第三轮次数。补答总计 6 次比较、6 次交换后确认无误,这次属于审题遗漏。
最后能说明标记为什么每轮重置、计数器为什么全程累计;也能指出把 compare_count++ 放到交换分支里,实际就变成了交换次数。判断有序要等完整一轮结束,因为下一对仍可能需要交换。
下一课是 C 第 24 天阶段检测,复习第 17—23 天的数组、输入遍历、最值、长度与平均值、查找计数、交换反转和冒泡排序。