c语言冒泡排序(含循环和递归)

一 、概念

冒泡排序(Bubble sort)顾名思义就是像气泡上升的过程,是一种计算机科学领域的较简单的排序算法,它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。 走访元素的工作是重复地进行,直到没有相邻元素需要交换,也就是说该元素列已经排序完成。(网上的定义)简单点说就是相邻两个数字大小比较,将较大的数放在后边,然后依次如此进行排序,过程比较像气泡上升,也就是小气泡上浮快,较大的气泡相对就下沉了。因此就叫冒泡法。定义比较容易理解。
比如有一组数:44、12、7、45、56、5、1
第一次比较是12和44进行,12是小于44,因此顺序换成12、44,然后是44和7比较,交换,之后都如此做。
第一趟进行后:12、7、44、45、5、1、56
一共比较了6次。
第二趟进行后:7、12、44、5、1、45、56
最后一共执行了6趟(也就是n-1趟,其中n是数字的总数)

二、代码实现

1. 循环

假设输入十个数,比较大小,输出顺序,那么代码如下所示:

#include<stdio.h>
int main()
{
	int i, j , h , temp;//其中i是为了输入输出十个数
	//h为比较的趟数
	//j为每趟比较的次数
	//temp是一个临时变量,用于互换相邻两数的大小
	int a[10];
	//输入十个数
	for (i = 0; i < 10; i++)
	{
		scanf_s("%d", &a[i]);
	}
	for(h = 9;h > 0;h --)
	{
		for (j = 0; j < h; j++)
		{
			if (a[j] > a[j + 1])
			{
				temp = a[j + 1];
				a[j + 1] = a[j];
				a[j] = temp;//互换大小
			}
		}
	}
	
	for (i = 0; i < 10; i++)
	{
		printf("%d", a[i]);//输出排好序的十个数
		printf("\n");
	}
	return 0;
}

对于核心代码

for(h = 9;h > 0;h --)
{
	for (j = 0; j < h; j++)
	{
		if (a[j] > a[j + 1])
		{
			temp = a[j + 1];
			a[j + 1] = a[j];
			a[j] = temp;//互换
		}
	}
}

也可以换成下边的形式,主要是分清循环执行的次数就不会写错。

for (h = 1; h < 10 ; h++)
{
	for (j = 0; j < 10 - h; j++)
	{
		if (a[j] > a[j + 1])
		{
			temp = a[j + 1];
			a[j + 1] = a[j];
			a[j] = temp;//互换
		}
	}
}


最后是互换的代码:

if (a[j] > a[j + 1])
	{
		temp = a[j + 1];
		a[j + 1] = a[j];
		a[j] = temp;//互换大小
	}

2、递归

递归就是函数调用自身,跟循环类似但是代码会很简洁,但是会浪费空间(最典型的递归就是斐波那契数列),递归是函数的高级操作,一个大佬说过:“如果使用循环,程序的性能可能更高;如果使用递归,程序可能更容易理解。如何选择要看什么对你来说更重要。”,递归是从一个大的方面回推,会锻炼逻辑思维(就像是大事化小)如果想要完成某件事,那么可以先完成一个子事件,子事件又可以拆分。
书上关于递归的一个理解在这里插入图片描述
上面的图片可以帮助理解递归
对于冒泡排序,也可以递归实现,
例如比较五个数:5,4,3,2,1(排成正序)
第一趟 :4.,3,2,1,5
第二趟 :3,2,1,4,5
第三趟 :2,1,3,4,5
第四趟 :1,2,3,4,5
此时可以理解为想完成排序,那么得先完成第一趟排序,想完成第一趟就要先完成第二趟,想完成第二趟就要先完成第三趟……以此类推。
那么根据这个理解可以写出代码

#include<stdio.h>
void BubbleSort2(int* arr, int sz)//切记别写错了
{
	if (sz < 2)
		return;//如果小于两个元素不用排
	int  i ;
	for (i = 0; i < sz - 1; i++)
	{
		if (arr[i] > arr[i + 1])
		{
			int temp = arr[i];
			arr[i] = arr[i + 1];
			arr[i + 1] = temp;
		}
	}
	 BubbleSort2(arr, --sz);//先--
}

int main()
{
	int i = 0;
	int arr[] = { 5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	BubbleSort2(arr, sz);
	for (i = 0; i < sz; i++)
		{
			printf("%d", arr[i]);//输出
		}
	return 0;
}


使用递归可以简化代码,但是过程不容易想到

3、对指针的使用

如果你学习了指针相关的知识,那么就可以把代码的核心部分转化为一个函数BubbleSort从而使代码更具有条理性。通过指针进行传址,函数的核心跟上文写的代码类似,就不过多解释了,直接上代码。

#include<stdio.h>
void BubbleSort(int arr[],int sz )
{
	int i = 0 ;
	for(i = 0 ;i <=sz ;i++)
	{
		int j = 0;
		for(j = 0 ;j <sz-i-1;j++)
			{
				if(arr[j] > arr[j+1])
				{
					int temp = arr[j];//排序
					arr[j] = arr[j+1];
					arr[j+1] = temp;
				}
			}
	}
}
	int main()
	{
	int i = 0;
	int arr[] = {9,8,7,6,5,4,3,1,2};
	int sz = sizeof(arr) / sizeof(arr[0]);
	BubbleSort( arr,sz);
	for(i = 0 ;i <sz ;i++)
		{
			printf("%d",arr[i]);
	
		}
	return 0 ;
}

以上就是转换成函数的形式的冒泡排序,输出排序后的数组也可以转化为函数,这里就不多赘述了

三、优化

如果所给数据是完全逆序的(要求排成正序)那么上边的冒泡很合适,但是如果所给数据部分有序,假设极端一些(1,2,3,4,5,6,7,9,8)大部分都是有序的,但是上边的程序却依旧会执行最繁琐情况的次数,所以很低效,因此可以引入一个flag,先把flag赋值为1,只要执行一次交换位置,那么flag就赋值为0,如果未交换位置,那么flag仍为1,此时跳出循环,就节省了循环执行的次数
代码如下(其实是上边的代码加上几个语句)

#include<stdio.h>
void BubbleSort(int arr[],int sz )
{
	int i = 0 ;
	for(i = 0 ;i <=sz ;i++)
	{
		int flag = 1 ;//增加部分
		int j = 0;
		for(j = 0 ;j <sz-i-1;j++)
			{
				if(arr[j] > arr[j+1])
				{
					int temp = arr[j];//排序
					arr[j] = arr[j+1];
					arr[j+1] = temp;
					flag = 0;//增加部分
				}
			}
		if(flag ==1)//增加部分
		{
		break;
		}	
	}
}
int main()
{
int i = 0;
int arr[] = {9,8,7,6,5,4,3,1,2};
int sz = sizeof(arr) / sizeof(arr[0]);
BubbleSort( arr,sz);
for(i = 0 ;i <sz ;i++)
	{
		printf("%d",arr[i]);
	}
return 0 ;
}


如果是这样执行会大大提高冒泡的效率(虽然也不高)
最后冒泡排序可以很好的加强我们对数组,函数,递归,指针等知识的深入理解,独立的写出来可以提升自己的代码能力。
最后的最后代码都执行过,按理说不会报错(vs2022版),但是如果某些地方不太严谨,请大佬指正,会及时修改。

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
C语言是编程语言中的一朵奇葩,虽已垂垂老矣,但却屹立不倒,诞生了数十年,仍然是最流行的编程语言之一。C语言看似简单,却不易吃透,想要运用好,更是需要积淀。本书是一本修炼C程序设计能力的进阶之作,它没有系统地去讲解C语言的语法和编程方法,而是只对C语言中不容易被初学者理解的重点、难点和疑点进行了细致而深入的解读,揭露了C语言中那些鲜为普通开发者所知的秘密,旨在让读者真正掌握C语言,从而编写出更高质量的C程序代码。 全书一共11章:第1章重点阐述了C语言中不易被理解的多个核心概念,很多初学者在理解这些概念时都会存在误区;第2~8章对预处理、选择结构和循环结构的程序设计、数组、指针、数据结构、函数和文件等知识点的核心问题和注意事项进行了讲解;第9章介绍了调试和异常处理的方法及注意事项;第10章对C语言中的若干容易让开发者误解误用的陷阱知识点进行了剖析;第11章则对所有程序员必须掌握的几种算法进行了详细的讲解;附录经验性地总结了如何养成良好的编码习惯,这对所有开发者都尤为重要。 本书主要内容:  堆和栈、全局变量和局部变量、生存期和作用域、内部函数和外部函数、指针变量、指针数组和数组指针、指针函数和函数指针、传址和传值、递归和嵌套、结构体和共用体、枚举、位域等较难理解的核心概念的阐述和对比;  预处理中的疑难知识点,包括文件的包方式、宏定义及其常见错误解析、条件编译指令和#pragma指令的使用等;  if、switch等选择结构语句的使用注意事项和易错点解析;  for、while、do while等循环结构语句的使用注意事项和易错点解析;  循环结构中break、continue、goto、return、exit的区别;  一维数组、二维数组、多维数组、字符数组、动态数组的定义和引用,以及操作数组时的各种常见错误解析;  不同类型的指针之间的区别,以及指针的一般用法和注意事项;  指针与地址、数组、字符串、函数之间的关系,以及指针与指针之间的关系;  枚举类型的使用及注意事项,结构体变量和共用体变量的初始化方法及引用;  传统链表的实现方法和注意事项,以及对传统链表实现方法的颠覆;  与函数参数、变参函数、函数调用、函数指针相关的一些难理解和容易被理解错的知识点解析;  文件和指针的使用原则、技巧和注意事项;  函数调用和异常处理的注意事项和最佳实践;  与strlen、sizeof、const、volatile、void、void*、#define、typedef、realloc、malloc、calloc等相关的一些陷阱知识点的解析;  时间复杂度、冒泡排序法、选择排序法、快速排序法、归并排序法、顺序排序法、二分查找等常用算法的详细讲解;  良好的编码习惯和编程风格。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lin_or_lin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值