NOIP-C++大神培养计划Step1.2.1——排序之冒泡排序

简介:

在我们解决一些问题时,我们需要将所给的数据排序。排序算法也是一门基础算法,排序有许多种:冒泡排序,插入排序,选择排序,桶排序,快速排序,归并排序,希尔排序,堆排序,拓扑排序等(最后两种以后学数据结构和图论再讲)。


今天,我们就来走进第一个排序——冒泡排序。


我们先来看一个图:


f63d585c0ac9843248b50b6876ec6594cad4faf1


这就是冒泡排序的基本原理。第一层循环从n~2,第二层循环枚举第j和第j+1个数(1<=j<i),若a[ij>a[j+1],就叫换他们俩。(这里是从小到大升序排列)


这样,每执行完一个外层循环就确定第i个数(2<=i<=n),最终方可之其结果。


让我们来看代码:


#include<iostream>
using namespace std;
int n,a[10001];
void swap(int &a,int &b)
{
	int t=a;
	a=b;
	b=t;
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=n;i>1;i--)
		for(int j=1;j<i;j++)
			if(a[j]>a[j+1])
				swap(a[j],a[j+1]);
	for(int i=1;i<=n;i++)
		cout<<a[i]<<" ";
	cout<<endl;
	return 0;
}



这个算法的复杂度是O(n^2),两层循环,时间复杂度还比较高。我们想来看看大数据的效率。


直接在代码中加一些代码:



#include<iostream>
#include<ctime>
using namespace std;
int n,a[10001];
void swap(int &a,int &b)
{
	int t=a;
	a=b;
	b=t;
}
int main()
{
	int start=clock();//开始时间 
	freopen("num.txt","r",stdin);//用freopen增加程序输入效率 
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=n;i>1;i--)
		for(int j=1;j<i;j++)
			if(a[j]>a[j+1])
				swap(a[j],a[j+1]);
	for(int i=1;i<=n;i++)
		cout<<a[i]<<" ";
	cout<<endl;
	int end=clock();//结束时间
	cout<<"Run time in"<<end-start<<"ms"<<endl; 
	return 0;
}


由于我们的电脑不同,所以所测试间不同。其实,我们这个算法还是不够好的,我们可以进行优化。


比如说6个数:2,1,4,6,3,5  我们来排序。


冒泡第一轮:


1324562308008596dbd5b0f4e97473c1d5da62f9

第二轮:

        fc27a9eea9aa207ad38f3a82bc62d1209cd6a348

接下来,我们不难发现,序列已经排好,之后的循环都是不必要的!!!

既然是不必要,我们就把它删了呗!


优化流程:

用一个bool变量判断是否有交换。初始化为0,交换了,就变成1。这一层循环结束之后,若它还是0,也就是没有交换,直接退出。


代码如下:


#include<iostream>
using namespace std;
int n,a[10001];
bool v=0;
void swap(int &a,int &b)
{
	int t=a;
	a=b;
	b=t;
}
int main()
{ 
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=n;i>1;i--){
		v=0;
		for(int j=1;j<i;j++)
			if(a[j]>a[j+1]){
				swap(a[j],a[j+1]);
				v=1;
			}
		if(!v)//v=0
			break;//跳出循环 
	}
	for(int i=1;i<=n;i++)
		cout<<a[i]<<" ";
	cout<<endl;
	return 0;
}

虽然不是每组数据都能优化,但是总有节约时间的地方。


我们还能将它写成函数形式:



void bubble_sort(int n,int a[]){
	for(int i=n;i>=1;i--){
		bool v=0;
		for(int j=1;j<i;j++)
			if(a[j]>a[j+1]){
				swap(a[j],a[j]+1);
				v=1;
			}
		if(!v){
			break;
		}
	}
	return;
}

到这里,冒泡排序的介绍就结束了。那么,大家会不会有疑问:这个冒泡排序效率不是最高的,也不是最方便的,学他干什么呢?不是浪费时间吗?


其实不是。任何一种算法都有他立足与信息学世界的理由。


冒泡排序的实质就是将相邻两个数交换,于是,我们可以利用这一点来求解特定的问题。


栗1.2.1-1 洛谷P1116 车厢重组

https://www.luogu.org/problemnew/show/P1116


题目描述

在一个旧式的火车站旁边有一座桥,其桥面可以绕河中心的桥墩水平旋转。一个车站的职工发现桥的长度最多能容纳两节车厢,如果将桥旋转180度,则可以把相邻两节车厢的位置交换,用这种方法可以重新排列车厢的顺序。于是他就负责用这座桥将进站的车厢按车厢号从小到大排列。他退休后,火车站决定将这一工作自动化,其中一项重要的工作是编一个程序,输入初始的车厢顺序,计算最少用多少步就能将车厢排序。

输入输出格式

输入格式:

共两行。

车厢总数N,N<=10000

第二行是N个不同的数表示初始的车厢顺序。

输出格式:

一个整数,最少的旋转次数。

输入输出样例

输入样例#1:
4
4 3 2 1 
输出样例#1:  
6




不难看出,这题要用冒泡排序来求解。

我们每交换一次,Ans++,表示旋转次数++。这题就迎刃而解了。

来看代码吧:


#include<iostream>
using namespace std;
int n,a[10001],Ans=0;
bool v=0;
void swap(int &a,int &b)
{
	int t=a;
	a=b;
	b=t;
}
int main()
{ 
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=n;i>1;i--){
		v=0;
		for(int j=1;j<i;j++)
			if(a[j]>a[j+1]){
				swap(a[j],a[j+1]);
				v=1;
				Ans++;
			}
		if(!v)
			break; 
	}
	cout<<Ans<<endl;
	return 0;
}

这题就这么过了吧,也没太大难度。

但是,我们不应该这么过。一位叫小学生的洛谷用户提供了这样的算法:

作者: 小学生 更新时间: 2018-04-26 08:44  在Ta的博客查看   


  • 我看了其他题解都是做了排序,可是题目只是问需要多少次移动,没问排序结果啊!!!

  • 所以我没有做排序,只是迭代去计算每个数字前有几个数字比它大,这意味着它必须要移动几次。

  • 没有做冒泡排序,双层循环写法也和冒泡无关。


#include <iostream>
using namespace std;
int n, sum;
int main()
{
    cin >> n;
    int a[n];
    for (int i = 0; i < n; ++i)
        cin >> a[i];
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < i; ++j)
            if (a[j] > a[i])
                ++sum;
    cout << sum;
    return 0;
}

我们看看他的算法。

第一眼望过去,貌似是错的。没有改变之前的状态,后面怎么排序呢?

好吧,他说了,不用排序。其实思想跟冒泡排序没太大差别。

我们想一想,假设有n个数,6,5,4,3,2,1。

在6后面,有5个比6小的数,就要旋转5次,

在5后面,有4个比5小的数,就要旋转4次,

······

在1后面,有0个比1小的数,就要旋转0次,

SO,Ans=0+1+2+3+4+5=15。

结果完全一样。

好了,今天的内容就到这里,我们下一期将给大家介绍插入排序,我们下次见!




相关文章
|
9月前
|
人工智能 算法 测试技术
【数学】【排序】【C++算法】3027人员站位的方案数
【数学】【排序】【C++算法】3027人员站位的方案数
|
9月前
|
搜索推荐 C++
C++冒泡排序的实现
C++冒泡排序的实现
|
1月前
|
存储 人工智能 算法
【C++数据结构——内排序】二路归并排序(头歌实践教学平台习题)【合集】
本关任务是实现二路归并算法,即将两个有序数组合并为一个有序数组。主要内容包括: - **任务描述**:实现二路归并算法。 - **相关知识**: - 二路归并算法的基本概念。 - 算法步骤:通过比较两个有序数组的元素,依次将较小的元素放入新数组中。 - 代码示例(以 C++ 为例)。 - 时间复杂度为 O(m+n),空间复杂度为 O(m+n)。 - **测试说明**:平台会对你编写的代码进行测试,提供输入和输出示例。 - **通关代码**:提供了完整的 C++ 实现代码。 - **测试结果**:展示代码运行后的排序结果。 开始你的任务吧,祝你成功!
36 10
|
1月前
|
搜索推荐 算法 数据处理
【C++数据结构——内排序】希尔排序(头歌实践教学平台习题)【合集】
本文介绍了希尔排序算法的实现及相关知识。主要内容包括: - **任务描述**:实现希尔排序算法。 - **相关知识**: - 排序算法基础概念,如稳定性。 - 插入排序的基本思想和步骤。 - 间隔序列(增量序列)的概念及其在希尔排序中的应用。 - 算法的时间复杂度和空间复杂度分析。 - 代码实现技巧,如循环嵌套和索引计算。 - **测试说明**:提供了测试输入和输出示例,帮助验证代码正确性。 - **我的通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了代码运行的测试结果。 通过这些内容,读者可以全面了解希尔排序的原理和实现方法。
58 10
|
1月前
|
搜索推荐 C++
【C++数据结构——内排序】快速排序(头歌实践教学平台习题)【合集】
快速排序是一种高效的排序算法,基于分治策略。它的主要思想是通过选择一个基准元素(pivot),将数组划分成两部分。一部分的元素都小于等于基准元素,另一部分的元素都大于等于基准元素。然后对这两部分分别进行排序,最终使整个数组有序。(第一行是元素个数,第二行是待排序的原始关键字数据。本关任务:实现快速排序算法。开始你的任务吧,祝你成功!
41 7
|
1月前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
40 5
|
8月前
|
C++ 容器
C++之deque容器(构造、赋值、大小、插入与删除、存取、排序)
C++之deque容器(构造、赋值、大小、插入与删除、存取、排序)
|
8月前
|
算法 搜索推荐 C++
C++之STL常用算法(遍历、查找、排序、拷贝、替换、算数生成、集合)
C++之STL常用算法(遍历、查找、排序、拷贝、替换、算数生成、集合)
|
8月前
|
C++
C++数组(定义、遍历、长度、地址、最大值、逆置、冒泡排序)
C++数组(定义、遍历、长度、地址、最大值、逆置、冒泡排序)
123 0
|
9月前
|
算法 测试技术 C#
【模拟】【C++算法】2826. 将三个组排序
【模拟】【C++算法】2826. 将三个组排序