[排序篇] 冒泡排序

目录

一、概念

二、冒泡排序

2.1 冒泡降序(从大到小排序)

2.2 冒泡升序(从小到大排序)

三、冒泡排序应用 

总结 


一、概念

冒泡排序核心思想:每次比较两个相邻的元素,如果它们不符合排序规则(升序或降序)则把它们交换过来。

二、冒泡排序

2.1 冒泡降序(从大到小排序)

冒泡降序:每次比较相邻的两个数,如果后面的数比前面的数大,则交换这两个数的位置。 

假设将 12 18 76 35 99 这 5 个数进行从大到小的排序(即,越小的越靠后)

如上图所示,从左往右逐列看,5 个数总共需要遍历 4 次(即 n - 1 次);而每列从上往下逐行看,每遍历一次总共需要排序 n - i 次(i 代表遍历的次数)。

1. 首先看第一列: 

1.1 第一行:比较第 1 位和第 2 位的大小,发现 12 比 18 要小,因为是降序,所以需要交换这两个数的位置。交换之后这 5 个数的顺序是 18 12 76 35 99;

1.2 第二行:比较第 2 位和第 3 位的大小,发现 12 比 76 要小,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 18 76 12 35 99;

1.3 第三行:比较第 3 位和第 4 位的大小,发现 12 比 35 要小,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 18 76 35 12 99;

1.4 第三行:比较第 4 位和第 5 位的大小,发现 12 比 99 要小,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 18 76 35 99 12; 

遍历第一次并经过 4 次排序后,5 个数中最小的一个 12 已经归位到队列的最后一位了(即第 5 位)。

2. 再看第二列: 

2.1 第一行:比较第 1 位和第 2 位的大小,发现 18 比 76 要小,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 76 18 35 99 12

2.2 第二行:比较第 2 位和第 3 位的大小,发现 18 比 35 要小,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 76 35 18 99 12

2.3 第三行:比较第 3 位和第 4 位的大小,发现 18 比 99 要小,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 76 35 99 18 12

遍历第二次并经过 3 次排序后,5 个数中倒数第二小的一个 18 已经归位到队列的倒数第二位了(即第 4 位)。 

3. 再看第三列: 

2.1 第一行:比较第 1 位和第 2 位的大小,发现 76 比 35 要大,则不需要交换这两个数的位置。并且这 5 个数的顺序仍然是 76 35 99 18 12

2.2 第二行:比较第 2 位和第 3 位的大小,发现 35 比 99 要小,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 76 99 35 18 12

遍历第三次并经过 2 次排序后,5 个数中倒数第三小的一个 35 已经归位到队列的倒数第三位了(即第 3 位)。  

3. 最后看第四列: 

2.1 第一行:比较第 1 位和第 2 位的大小,发现 76 比 99 要小,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 99 76 35 18 12

遍历第四次并经过 1 次排序后,5 个数中倒数第四小的一个 76 已经归位到队列的倒数第四位了(即第 2 位)。 

最后总结一下:如果有 n 个数进行排序,只需将 n-1 个数归位,也就是说要进行 n-1 次操作。而 “每一次” 都需要从第 1 位开始进行相邻两个数的比较,将较小的一个数放在后面,比较完毕后向后移一位继续比较下面两个相邻数的大小,重复此步骤,直到最后一个尚未归位的数,已经归位的数则无需再进行比较。

冒泡降序例程1(推荐): 

#include <stdio.h>void main()
{int arr[5];int n;int tmp;scanf("%d", &n); //输入一个数n,表示接下来有n个数for (int i = 0; i < n; i++) //循环读入n个数到数组arr中scanf("%d", &arr[i]);//冒泡降序的核心代码for (int i = 1; i <= n - 1; i++) { //n个数排序,只需进行 n-1 次(i从1开始,因此i需要包含 n-1)for (int j = 0; j < n - i; j++) { //i从1开始,则j从第1位(数组0下标)开始与后面一个数比较,直到最后一个尚未归位的数(已归位的数无需再比较,因此只需排序 n-i 次)if (arr[j] < arr[j+1]) { //相邻两个数比较大小并交换tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}//输出结果for (int i = 0; i < n; i++)printf("%d ", arr[i]);printf("\n");
}

冒泡降序例程2: 

#include <stdio.h>void main()
{int arr[5];int n;int tmp;scanf("%d", &n); //输入一个数n,表示接下来有n个数for (int i = 0; i < n; i++) //循环读入n个数到数组arr中scanf("%d", &arr[i]);//冒泡降序的核心代码for (int i = 0; i < n - 1; i++) { //n个数排序,只需进行 n-1 次(i从0开始,因此i不能包含 n-1)for (int j = 1; j < n - i; j++) { //i从0开始,则j从第2位(即数组1下标)开始与前面一个数比较,直到最后一个尚未归位的数(已归位的数无需再比较,因此只需排序 n-i 次)if (arr[j-1] < arr[j]) { //相邻两个数比较大小并交换tmp = arr[j-1];arr[j-1] = arr[j];arr[j] = tmp;}}}//输出结果for (int i = 0; i < n; i++)printf("%d ", arr[i]);printf("\n");
}

冒泡降序例程3:  

#include <stdio.h>void main()
{int arr[5];int n;int tmp;scanf("%d", &n); //输入一个数n,表示接下来有n个数for (int i = 0; i < n; i++) //循环读入n个数到数组arr中scanf("%d", &arr[i]);//冒泡降序的核心代码for (int i = 0; i < n - 1; i++) { //n个数排序,只需进行 n-1 次 (i从0开始,因此i不能包含 n-1)for (int j = 0; j < n - i - 1; j++) { //从第1位(数组0下标)开始与后面一个数比较,直到最后一个尚未归位的数(已归位的数无需再比较,然而i和j都是从0开始,因此只需排序 n-i-1 次)if (arr[j] < arr[j+1]) { //相邻两个数比较大小并交换tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}//输出结果for (int i = 0; i < n; i++)printf("%d ", arr[i]);printf("\n");
}

2.2 冒泡升序(从小到大排序)

冒泡升序:每次比较相邻的两个数,如果后面的数比前面的数小,则交换这两个数的位置。 

假设将 99 35 18 76 12 这 5 个数进行从小到大的排序(即,越大的越靠后)

如上图所示,从左往右逐列看,5 个数总共需要遍历 4 次(即 n - 1 次);而每列从上往下逐行看,每遍历一次总共需要排序 n - i 次(i 代表遍历的次数)。

1. 首先看第一列: 

1.1 第一行:比较第 1 位和第 2 位的大小,发现 99 比 35 要大,因为是升序,所以需要交换这两个数的位置。交换之后这 5 个数的顺序是 35 99 18 76 12;

1.2 第二行:比较第 2 位和第 3 位的大小,发现 99 比 18 要大,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 35 18 99 76 12;

1.3 第三行:比较第 3 位和第 4 位的大小,发现 99 比 76 要大,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 35 18 76 99 12;

1.4 第三行:比较第 4 位和第 5 位的大小,发现 99 比 12 要大,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 35 18 76 12 99

遍历第一次并经过 4 次排序后,5 个数中最大的一个 99 已经归位到队列的最后一位了(即第 5 位)。

2. 再看第二列: 

2.1 第一行:比较第 1 位和第 2 位的大小,发现 35 比 18 要大,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 18 35 76 12 99

2.2 第二行:比较第 2 位和第 3 位的大小,发现 35 比 76 要小,则不需要交换这两个数的位置。并且这 5 个数的顺序仍然是 18 35 76 12 99

2.3 第三行:比较第 3 位和第 4 位的大小,发现 76 比 12 要大,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 18 35 12 76 99

遍历第二次并经过 3 次排序后,5 个数中第二大的一个 76 已经归位到队列的倒数第二位了(即第 4 位)。 

3. 再看第三列: 

2.1 第一行:比较第 1 位和第 2 位的大小,发现 18 比 35 要小,则不需要交换这两个数的位置。并且这 5 个数的顺序仍然是 18 35 12 76 99

2.2 第二行:比较第 2 位和第 3 位的大小,发现 35 比 12 要大,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 18 12 35 76 99

遍历第三次并经过 2 次排序后,5 个数中第三大的一个 35 已经归位到队列的倒数第三位了(即第 3 位)。  

3. 最后看第四列: 

2.1 第一行:比较第 1 位和第 2 位的大小,发现 18 比 12 要大,因此需要交换这两个数的位置。交换之后这 5 个数的顺序是 12 18 35 76 99

遍历第四次并经过 1 次排序后,5 个数中第四大的一个 18 已经归位到队列的倒数第四位了(即第 2 位)。 

最后总结一下:如果有 n 个数进行排序,只需将 n-1 个数归位,也就是说要进行 n-1 次操作。而 “每一次” 都需要从第 1 位开始进行相邻两个数的比较,将较大的一个数放在后面,比较完毕后向后移一位继续比较下面两个相邻数的大小,重复此步骤,直到最后一个尚未归位的数,已经归位的数则无需再进行比较。

冒泡升序例程1(推荐): 

#include <stdio.h>void main()
{int arr[5];int n;int tmp;scanf("%d", &n); //输入一个数n,表示接下来有n个数for (int i = 0; i < n; i++) //循环读入n个数到数组arr中scanf("%d", &arr[i]);//冒泡降序的核心代码for (int i = 1; i <= n - 1; i++)) { //n个数排序,只需进行 n-1 次(i从1开始,因此i需要包含 n-1)for (int j = 0; j < n - i; j++) { //i从1开始,则j从第1位(数组0下标)开始与后面一个数比较,直到最后一个尚未归位的数(已归位的数无需再比较,因此只需排序 n-i 次)if (arr[j] > arr[j+1]) { //相邻两个数比较大小并交换tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}//输出结果for (int i = 0; i < n; i++)printf("%d ", arr[i]);printf("\n");
}

冒泡升序例程2:  

#include <stdio.h>void main()
{int arr[5];int n;int tmp;scanf("%d", &n); //输入一个数n,表示接下来有n个数for (int i = 0; i < n; i++) //循环读入n个数到数组arr中scanf("%d", &arr[i]);//冒泡降序的核心代码for (int i = 0; i < n - 1; i++) { //n个数排序,只需进行 n-1 次(i从0开始,因此i不能包含 n-1)for (int j = 1; j < n - i; j++) { //i从0开始,则j从第2位(即数组1下标)开始与前面一个数比较,直到最后一个尚未归位的数(已归位的数无需再比较,因此只需排序 n-i 次)if (arr[j-1] > arr[j]) { //相邻两个数比较大小并交换tmp = arr[j-1];arr[j-1] = arr[j];arr[j] = tmp;}}}//输出结果for (int i = 0; i < n; i++)printf("%d ", arr[i]);printf("\n");
}

冒泡升序例程3: 

#include <stdio.h>void main()
{int arr[5];int n;int tmp;scanf("%d", &n); //输入一个数n,表示接下来有n个数for (int i = 0; i < n; i++) //循环读入n个数到数组arr中scanf("%d", &arr[i]);//冒泡降序的核心代码for (int i = 0; i < n - 1; i++) { //n个数排序,只需进行 n-1 次 (i从0开始,因此i不能包含 n-1)for (int j = 0; j < n - i - 1; j++) { //从第1位(数组0下标)开始与后面一个数比较,直到最后一个尚未归位的数(已归位的数无需再比较,然而i和j都是从0开始,因此只需排序 n-i-1 次)if (arr[j] > arr[j+1]) { //相邻两个数比较大小并交换tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}//输出结果for (int i = 0; i < n; i++)printf("%d ", arr[i]);printf("\n");
}

三、冒泡排序应用 

假设一个班有 5 个学生,需要将这 5 个学生期末考试的分数,从高到低排序,并且输出对应的学生姓名和性别。大家可以思考一下,该如何实现?

根据 2.1 冒泡降序原理,在此,我们只需要声明一个结构体,其成员包含学生的姓名、性别和分数(假设满分为 100,并分数只有整数)。下面是实际的例子。

#include <stdio.h>struct student {char name[24];char sex[8];int score;
};void main()
{struct student stu[5];struct student tmp;int n;scanf("%d", &n); //输入班级总人数for (int i = 0; i < n; i++) //循环读入学生总数到数组stu中scanf("%s %s %d", stu[i].name, stu[i].sex, &stu[i].score);//开始对学生进行分数排序for (int i = 1; i <= n - 1; i++) {for (int j = 0; j < n - i; j++) {if (stu[j].score < stu[j+1].score) { //比较相邻的两个数,分数高的排前面tmp = stu[j];stu[j] = stu[j+1];stu[j+1] = tmp;}}}//输出结果for (int i = 0; i < n; i++)printf("%s %s %d", stu[i].name, stu[i].sex, &stu[i].score);printf("\n");
}

可以输入以下数据进行验证:

5

李文 男 80
韩飞 男 50
晓晓 女 86
胡峰 男 78
陈肖 女 66

运行结果是: 

晓晓 女 86

李文 男 80
胡峰 男 78
陈肖 女 66
韩飞 男 50

总结 

1. 冒泡降序:每次比较相邻的两个数,如果后面的数比前面数大,则交换这两个数的位置;

2. 冒泡升序:每次比较相邻的两个数,如果后面的数比前面数小,则交换这两个数的位置;

3. 从例程代码来看,可知冒泡排序有很多种方法,但是万变不离其宗,都是围绕 “如果有 n 个数进行排序,则需遍历 n-1 次,而 “每一次” 需要排序 n-i 次,并且都是从第 1 位开始进行相邻两个数的比较,将较小或较大的一个数放在后面,如此重复,直到最后一个尚未归位的数” 展开。

4. “冒泡降序” 与 “冒泡升序” 例程代码的唯一差异是:相邻的两个数较小或较大的放在后面。例如,if (arr[j] < arr[j+1]) 或 if (arr[j] > arr[j+1]);

5. 冒泡排序的核心部分是双重嵌套循环。不难看出冒泡排序的时间复杂度是 O(N^{2})。这是一个非常高的时间复杂度。正如 Donald E. Knuth 所说:“冒泡排序除了它迷人的名字和导致了某些有趣的理论问题这一事实之外,似乎没有什么值得推荐的。”

那还有没有更好的排序算法呢?有,请看下章节——快速排序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/267138.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【桌面应用开发】Tauri是什么?基于Rust的桌面应用

自我介绍 做一个简单介绍&#xff0c;酒架年近48 &#xff0c;有20多年IT工作经历&#xff0c;目前在一家500强做企业架构&#xff0e;因为工作需要&#xff0c;另外也因为兴趣涉猎比较广&#xff0c;为了自己学习建立了三个博客&#xff0c;分别是【全球IT瞭望】&#xff0c;【…

网络测试工具:tcping-测试端口连接

网络测试工具&#xff1a;tcping-测试端口连接 平常使用的ping&#xff0c;是通过icmp协议去测试网络连通性的&#xff0c;tcping是通过tcp三次握手测试端口的连通性。总的来说&#xff0c;ping测试的是L3的连通性&#xff0c;tcping测试的是L4的连通性。 tcping工具下载 htt…

西工大网络空间安全学院计算机系统基础实验二(phase_2下——漫漫深夜过后的黎明!!!)

内存地址内存地址中的数注释指向这块内存的寄存器0xffffd0e8函数phase_2的栈帧0xffffd0e40xffffd0f4函数phase_2的栈帧0xffffd0e00x5655b7b0函数phase_2的栈帧0xffffd0dc0x565566ca函数read_six_numbers的返回地址&#xff0c;函数phase_2的栈帧0xffffd0d80x5655af64旧%ebx的值…

网络基础(七):传输层协议介绍

目录 一、TCP协议&#xff08;传输控制协议&#xff09; 1、TCP协议介绍 2、TCP协议特性 3、TCP报文格式 4、TCP的三次握手 4.1TCP三次握手的概念 4.2TCP三次握手流程图 4.3 TCP三次握手阐释说明 5、TCP的四次挥手 5.1TCP四次挥手的概念 5.2TCP四次挥手的流程图 5.…

qt-C++笔记之addAction和addMenu的区别以及QAction的使用场景

qt-C笔记之addAction和addMenu的区别以及QAction的使用场景 code review! 文章目录 qt-C笔记之addAction和addMenu的区别以及QAction的使用场景1.QMenu和QMenuBar的关系与区别2.addMenu和addAction的使用场景区别3.将QAction的信号连接到槽函数4.QAction的使用场景5.将例1修改…

用 CSS 写一个渐变色边框的输入框

Using_CSS_gradients MDN 多渐变色输入框&#xff0c;群友问了下&#xff0c;就试着写了下&#xff0c;看了看 css 渐变色 MDN 文档&#xff0c;其实很简单&#xff0c;代码记录下&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta ch…

Error: Failed to resolve vue/compiler-sfc——vite项目启动报错——npm run serve

运行项目时&#xff0c;报错如下&#xff1a; Error: Failed to resolve vue/compiler-sfc 根据报错信息的提示&#xff1a;vue的版本必须大于3.2.25&#xff0c;经过查看package.json文件&#xff0c;可以看到vue的版本为3.2.36&#xff0c;是满足条件的。 因此考虑缓存问题&…

12/11

完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和密码不匹配&#xf…

【AI】ChatGLM3-6B上手体验

之前写过ChatGLM2-6B大语言模型的部署安装文档&#xff0c;现在ChatGLM模型已经更新迭代到第三代了&#xff0c;从官方公布的数据来看&#xff0c;模型的能力是得到了进一步的增强。 这次写文章主要是来记录一下使用过程&#xff0c;方便回头查看。 ChatGLM3-6B官方的视频教程…

HTML常用表单元素使用?

目录 一、常用表单元素使用的关键字二、常用表单元素使用的效果与作用&#xff08;1&#xff09;password : 保护用户的隐私(2) email: 输入邮件&#xff08;比如QQ邮件&#xff09;(3)、number : 输入框只能输入数字&#xff08;4&#xff09;、tel : 常用于输入电话号&#x…

ubuntu install sqlmap

refer: https://github.com/sqlmapproject/sqlmap 安装sqlmap&#xff0c;可以直接使用git 克隆整个sqlmap项目&#xff1a; git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev 2.然后进入sqlmap-dev&#xff0c;使用命令&#xff1a; python s…

【C语言程序设计】顺序结构程序设计

目录 前言 一、程序阅读 二、程序改错 三、程序设计 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首发于CSDN&#x1f4da;。 &#x1f4e3;如需转…