前言:

冒泡排序可以说是排序系列中最简单也最基础的一种排序的方式,尽管它十分的简单易懂,但依旧会有一些小问题是大家可能忽略的,因此我打算将不同排序分成单独的文章进行讲解,这样既不会显得臃肿,同时也可以讲得更加仔细,废话不多说,开干。

一.概念简述

从上图我们不难看出冒泡排序应该有两个循环:第一个循环是小循环,该循环的作用是——在某个数组内依次进行两个数的大小比较;第二个循环是大循环,该循环的作用是——决定小循环的次数

那么概括来说就是,在一定次数内,数组按照一定的大小顺序进行两两比较,满足顺序的移动

基本代码:

既然已经了解了冒泡排序的执行方式,那么就不难写出代码了,那么先直接给出代码:

#include<stdio.h>
const int N = 1e5;
int a[N];

void swap(int &x,int &y){
	int t = x;
	x = y;
	y = t;
}//交换数字函数
//这是降序
int main()
{
	int n;
	scanf("%d",&n);
	for(int i = 0; i < n; i ++)scanf("%d",&a[i]);
    //冒泡排序
	for(int i = 1; i <= n; i ++){
		for(int j = 0; j < n; j ++){
			if(a[j]<a[j + 1])swap(a[j],a[j + 1]);
		}
	}

	for(int i = 0; i < n; i ++)printf("%d",a[i]);
	return 0;
}

接下来是对该代码的分析:

1.大循环:

for(int i = 1; i <= n; i++)

大循环的作用是决定小循环的次数,可次数是多少呢?其实我们不难看出每经过一次循环就会有一个数被固定在右边,也就是图片上的黄色部分,所以我们可以得出次数其实就是一个数组内元素的个数,这样大循环就建立起来了;

2.小循环:

for(int j = 0; j < n; j ++){
			if(a[j]<a[j - 1])swap(a[j],a[j - 1]);
		}

小循环很简单,就是两两比较,进行交换。

代码优化:

优化一:

小循环的顺序:通常来说我们都采用从0开始循环。对于一般题来说没有问题,但如果你用冒泡排序做字符串排序,并且排序方式是升序,那么就会出现问题。

#include<stdio.h>
#include<string.h>
void swap(char &x,char &y){
	char t = x;
	x = y;
	y = t;
}
int main()
{
    char a[100];
    scanf("%s",a);
    int len = strlen(a);

	for(int i = 1; i <= len; i ++){
		for(int j = 0; j < len; j ++){
			if(a[j]>a[j + 1])swap(a[j],a[j + 1]);
		}
	}

	puts(a);
	return 0;
}

这个是一个将字符串以升序的方式进行排列的代码,乍一看没有问题,实际上暗藏陷阱。

当我们输入“zxcvb”,得到的结果却是一个空行,那么这是为什么呢?

我们先解析一下字符串:

{'z','x','c','v','b','/0'};

 结果已经很明显了,就是因为每个字符串都是以'/0'结尾的,而这个是会被编译器识别的,同时它又比前面的所有字符都小,因此它毫不意外的就会被放到最前面,得到的结果就是

{'/0','z','x','c','v','b'};

 而当我们编辑器输出时,一旦遇到‘/0’,就等于是告诉它,可以了,你不用在往后运行了。所有最后我们看到的就是一片空白。

那么改良过的代码就是:

#include<stdio.h>
const int N = 1e5;
int a[N];

void swap(int &x,int &y){
	int t = x;
	x = y;
	y = t;
}//交换数字函数
//这是降序
int main()
{
	int n;
	scanf("%d",&n);
	for(int i = 0; i < n; i ++)scanf("%d",&a[i]);
    //冒泡排序
	for(int i = 1; i <= n; i ++){
		for(int j = n-1; j >= 0; j --){
			if(a[j]<a[j - 1])swap(a[j],a[j - 1]);
		}
	}

	for(int i = 0; i < n; i ++)printf("%d",a[i]);
	return 0;
}

其实大体没变,就是将小循环变成了从后开始循环。

优化二:减少运行时间

其实通过最上面的动态图片我们可以看到,每经过一次大循环,就会有一个数被放在了最左面,而这个数在之后的循环中是不会在发生改变的,而这也是我们优化的出发点。

#include<stdio.h>
const int N = 1e5;
int a[N];

void swap(int &x,int &y){
	int t = x;
	x = y;
	y = t;
}//交换数字函数
//这是降序
int main()
{
	int n;
	scanf("%d",&n);
	for(int i = 0; i < n; i ++)scanf("%d",&a[i]);
    //冒泡排序
	for(int i = 1; i <= n; i ++){
		for(int j = n-1; j >= i; j --){
			if(a[j]<a[j - 1])swap(a[j],a[j - 1]);
		}
	}

	for(int i = 0; i < n; i ++)printf("%d",a[i]);
	return 0;
}

其实这个代码只改了一小个地方,就是将小循环的 (j >= 0)改成了(j >= i),为啥可以这样改呢。因为之前我们讲过大循环的意义是决定小循环的次数,那么i就代表了这是第几次循环,也就代表了已经有几个数被固定在了左边(根据优化一可知我们已经把小循环改成了从左到右循环,所以固定在了左边),而这些被固定的数其实是不需要再进行两两比较的。

给出最终的优化代码:

#include<stdio.h>
const int N = 1e5;
int a[N];

void swap(int &x,int &y){
	int t = x;
	x = y;
	y = t;
}//交换数字函数
//这是降序
int main()
{
	int n;
	scanf("%d",&n);
	for(int i = 0; i < n; i ++)scanf("%d",&a[i]);
    //冒泡排序
	for(int i = 1; i <= n; i ++){
		for(int j = n-1; j >= i; j --){
			if(a[j]<a[j - 1])swap(a[j],a[j - 1]);
		}
	}

	for(int i = 0; i < n; i ++)printf("%d",a[i]);
	return 0;
}

感谢你的阅读,本人蒟蒻,如果有不正确之处欢迎指出。下一个排序是桶排。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐