冒泡排序作为c语言基础入门的算法,也是很多新手的第一个算法,接下来我将以一个例子结合我的理解来讲述,什么是冒泡排序,它是如何使用的。
什么是冒泡排序?
冒泡排序是一种简单的排序算法,它重复地走访要排序的元素列表,一次比较两个相邻的元素,并且如果它们的顺序错误就把它们交换过来。走访列表的工作是重复地进行直到没有再需要交换,也就是说该列表已经排序完成。
冒泡排序的基本思想是:
- 从第一个元素开始,依次比较相邻的两个元素,如果顺序不正确(比如按照升序排列,但是前面的元素比后面的大),就交换它们的位置,这样一轮下来,最大的元素就会“冒泡”到最后面。
- 对剩余的元素重复以上步骤,每次内循环结束,最大的元素都会被放置到当前未排序部分的最后面。
- 重复上述步骤,每次都会将当前未排序部分的最大元素放置到相应位置,直到所有元素都排好序。
尽管冒泡排序的时间复杂度较高,但它的实现非常简单,适用于小规模的数据排序。
题目:
在一个内含多个元素的数组中,将内部元素从小到大的依次排列出来
我对代码进行了详细的注释,大家可以将代码复制到vs编译器里面观看,得到更加的观看效果
//冒泡排序
//两两相邻的元素进行比较
//如果不满足顺序就交换,满足顺序就找下一对
//外层循环表示排的次数
//内层循环表示每次的比较数据
#include <stdio.h>
int main()
{
// 初始化一个包含 10 个整数的数组
int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
// 使用冒泡排序算法对数组进行排序
for (int i = 0; i < 9; i++)
{
// 外循环,控制比较轮数,因为每次最大的数都会沉到最后,所以只需要比较 n-1 轮即可
for (int j = 0; j < 10 - 1 - i; j++)
{
// 内循环,控制每轮比较的次数,每轮比较相邻的两个数
if (arr[j] > arr[j + 1])
{
// 如果前一个数大于后一个数,就交换它们的位置,使得较大的数往后移动
int t = arr[j];
// 临时变量 t 用于交换两个数的值
arr[j] = arr[j + 1];
// 将后一个数赋值给前一个数
arr[j + 1] = t;
// 将临时变量中存储的前一个数的值赋值给后一个数,完成交换
}
}
}
// 打印排序后的数组
for (int i = 0; i < 10; i++)
{
printf("%d\t", arr[i]); // 以制表符分隔的形式打印数组中的每个元素
}
return 0;
}
//疑点1
//为什么第一个for循环中,循环次数是9(i<9)
//模拟一下循环过程
//第一轮循环:
//在第一轮循环中,面对10个数,我们需要进行9次比较
//进行之后,最大的数9已经变到最底部了
//第二论循环中,我们就只需要进行8次比较了,因为第一轮的比较已经将最大的数字沉底
//进行之后,现在最大的数字8也就到了底部
//在这个例子中,我们可以看到在每一轮的循环中,最后一个元素都会背确定为当前轮次的最大值
//因此我们可以在下一轮循环时少进行一次比较
//所以我们循环的表达式设置为i<9
//疑点2
//为什么内层循环,循环的上限是 10-1-i
//10是数组的长度,i是外层循环的当前轮次
//具体来说,假设我们有一个长度为10的数组,当前外层循环的轮次是0
//在第一轮循环结束的时候,数组的最后一个元素已经是最大的了,因此不用参加比较
//所以内层循环的上限就是 10-1-0
//当外层循环进行到第二轮的时候,i=1;
//在第二轮循环结束后,数组的倒数第二大的元素已经是次大,因此不需要再次参加比较
//此时内层循环的上限就是 10-1-1
//类推,随着外层循环的进行,每一轮内层循环的上限都会逐渐减小
//疑点3
//如果数组内部的排序,不是9876543210,第一个数字不是最大值,冒泡排序的方法还可行吗?
//在给定的冒泡排序算法中,只要数组中的数字排列符合比较规则,即较大的数字在前,较小的数字在后,那么无论数组中的第一个数字是什么,排序的输出结果都将是按照从大到小的顺序排列。
//冒泡排序的核心思想是比较相邻的两个元素,如果前面的元素大于后面的元素,则交换它们,这样一轮比较下来,最大的元素就会“冒泡”到数组的最前面。因此,即使数组中的第一个数字不是9,只要数组中的数字符合比较规则,经过多次冒泡操作后,最终的输出结果仍然会是按照从大到小的顺序排列的。
//所以,无论数组中的第一个数字是什么,给定的冒泡排序算法都能够得到预期的输出结果。
//写法2
#include<stdio.h>
void bubble_sort(int arr[], int sz)
{
int i = 0;
//趟数
for (i = 0; i < sz - 1; i++)
{
//一趟冒泡排序
int j = 0;
for (j = 0; j < sz-1-i; j++)
{
if (arr[j] < arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
void print_arr(int arr[], int sz)
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
//元素个数必须在主函数里面求,求好了之后再传给自定义函数。(形参和实参)
//在c语言中,数组作为参数传递给函数时,实际上传递的是数组的地址,而不是整个数组
//因此在自定义的函数中获取数组的大小,就无法直接通过参数传递数组来获取数组的大小信息
//在主函数中计算数组的大小,原因是,主函数是程序的入口,可以直接获取数组的大小,并将其传给所需要的函数
//自定义的函数中无法直接获取树的大小,因此无法在函数内部计算数组的大小
//也就是实参传递给形参
//在函数调用的时候,实参的值会被传递给函数的形参,形成了实参到形参的传递,形参是函数定义时声明的参数
//形参的值是由函数调用时提供的实参决定的
//形参无法直接传递给实参(可以通过指针传址),两参之间的传递关系是 : 实参传给形参
bubble_sort(arr, sz);
print_arr(arr, sz);
return 0;
}
在简单的理解冒泡排序之后,不妨来试一试下面的问题
自己输入十个整数,再进行排序
#include<stdio.h>
int main()
{
int arr[10];
printf("请输入十个整数:\n");
for (int a = 0; a < 10; a++)
{
printf("请输入第 %d 个整数:", a + 1);
scanf_s("%d", &arr[a]);//不要错写为&arr[10]
}
//输入十个整数,存到数组arr中
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int t = arr[j];
arr[j] = arr[j+1];
arr[j + 1] = t;
}
}
}
for (int k = 0; k < 10; k++)
{
printf("%d ", arr[k]);
}
return 0;
}
上述中使用scanf_s是在vs2022编译器下进行的,也可以通过声明等方式解决。其他编译器,正常使用scanf即可。
感谢您的观看,祝您早日成为编程大牛。