c语言排序算法合集:桶排序、冒泡排序、快速排序

(1)桶排序

如果需要对数据范围在 0-1000 之间的整数进行排序,我们需要 1001 个桶,来表示 0~1000
之间每一个数出现的次数,这一点一定要注意。另外,此处的每一个桶的作用其实就是“标
记”每个数出现的次数,因此数组 book就是用来统计的桶,代码实现如下。

时间复杂度:代码中第 6 行的循环一共循环了 m 次(m 为桶的个数),第 9 行的代码循环了 n 次(n 为待排序数的个数),第 14 行和第 15 行一共循环了 m+n 次。所以整个排序算法一共执行了 m+n+m+n 次。我们用大写字母 O 来表示时间复杂度,因此该算法的时间复杂度是 O(m+n+m+n)即 O(2*(m+n))。我们在说时间复杂度的时候可以忽略较小的常数,最终桶排序的时间复杂度为 O(m+n)。还有一点,在表示时间复杂度的时候,n 和 m通常用大写字母即 O(M+N)。

缺点:如果要对人的分数进行比较,只能把分数打印出来,对应的人名无法对应打印。

#include <stdio.h>
//桶排序
int main()
{
	int book[1001],i,j,t,n;
	for(i=0;i<=1000;i++)
		book[i]=0;
	scanf("%d",&n);//输入一个数n,表示接下来有n个数
	for(i=1;i<=n;i++)//循环读入n个数,并进行桶排序
	{
		scanf("%d",&t); //把每一个数读到变量t中
		book[t]++; //进行计数,对编号为t的桶放一个小旗子
	}
	for(i=1000;i>=0;i--) //依次判断编号1000~0的桶
		 for(j=1;j<=book[i];j++) //出现了几次就将桶的编号打印几次
			printf("%d ",i);
	getchar();
	getchar();
	return 0;
}

(2)冒泡排序

冒泡排序的基本思想是:每次比较两个相邻的元素,如果它们的顺序错误就把它们交换 过来

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

时间复杂度:冒泡排序的核心部分是双重嵌套循环。不难看出冒泡排序的时间复杂度是 O(N 2)。这是一个非常高的时间复杂度。

#include <stdio.h>
//冒泡排序
int main()
{
	int a[100],i,j,t,n;
	 scanf("%d",&n); //输入一个数n,表示接下来有n个数
	for(i=1;i<=n;i++) //循环读入n个数到数组a中
		scanf("%d",&a[i]);
	//冒泡排序的核心部分
	 for(i=1;i<=n-1;i++) //n个数排序,只用进行n-1趟
	{
		for(j=1;j<=n-i;j++) //从第1位开始比较直到最后一个尚未归位的数,想一想为什么到n-i就可以了。
		{
		 if(a[j]<a[j+1]) //比较大小并交换
			{ t=a[j]; a[j]=a[j+1]; a[j+1]=t; }
		}
	}
	for(i=1;i<=n;i++) //输出结果
		printf("%d ",a[i]);

	getchar();	
	getchar();
	 return 0;
}

优点:除了能对数组进行比较,还可以把比较之后的数字对应的数组下标打印出来。代码如下:
冒泡排序可以说是我们学习的第一个真正的排序算法,并且解决了桶排序浪费空间的问题,但在算法的执行效率上却牺牲了很多,它的时间复杂度达到了 O(N2)。假如我们的计算机每秒钟可以运行 10 亿次,那么对 1 亿个数进行排序,桶排序只需要 0.1 秒,而冒泡排序则需要 1 千万秒,达到 115 天之久。

#include <stdio.h>
struct student
{
 char name[21];
 char score;
};//这里创建了一个结构体用来存储姓名和分数

int main()
{
	struct student a[100],t;
	 int i,j,n;
	 scanf("%d",&n); //输入一个数n
	for(i=1;i<=n;i++) //循环读入n个人名和分数
	  scanf("%s %d",a[i].name,&a[i].score); 
   //按分数从高到低进行排序
	for(i=1;i<=n-1;i++)
	{
		for(j=1;j<=n-i;j++)
		{
			if(a[j].score<a[j+1].score)//对分数进行比较
				{ t=a[j]; a[j]=a[j+1]; a[j+1]=t; }
		}
	}
	for(i=1;i<=n;i++)//输出人名
		printf("%s\n",a[i].name);

 getchar();getchar();
 return 0;
} 

(3)快速排序

快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

快速排序之所以比较快,是因为相比冒泡排序,每次交换是跳跃式的。每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边。这样在每次交换的时候就不会像冒泡排序一样只能在相邻的数之间进行交换,交换的距离就大得多了。因此总的比较和交换次数就少了,速度自然就提高了。当然在最坏的情况下,仍可能是相邻的两个数进行了交换。因此快速排序的最差时间复杂度和冒泡排序是一样的,都是 O(N2),它的平均时间复杂度为 O (NlogN)。其实快速排序是基于一种叫做“二分”的思想。

#include <stdio.h>
//快速排序
int a[101],n;//定义全局变量,这两个变量需要在子函数中使用
void quicksort(int left,int right)
{
	 int i,j,t,temp;
	 if(left>right)
		 return;

	 temp=a[left]; //temp中存的就是基准数
	 i=left;
	 j=right;
	 while(i!=j)
	 {
		 //顺序很重要,要先从右往左找
		 while(a[j]>=temp && i<j)
			j--;
		 //再从左往右找
		 while(a[i]<=temp && i<j)
			i++;
		 //交换两个数在数组中的位置
		 if(i<j)//当哨兵i和哨兵j没有相遇时
		 {
		 t=a[i];
		 a[i]=a[j];
		 a[j]=t;
		 }
	 }
		 //最终将基准数归位 
		 a[left]=a[i];
		 a[i]=temp;

		quicksort(left,i-1);//继续处理左边的,这里是一个递归的过程
		quicksort(i+1,right);//继续处理右边的,这里是一个递归的过程
}
int main()
{
	 int i,j,t;
	 //读入数据
	 scanf("%d",&n);
	 for(i=1;i<=n;i++)
		 scanf("%d",&a[i]);
	 quicksort(1,n); //快速排序调用

	 //输出排序后的结果
	 for(i=1;i<=n;i++)
		 printf("%d ",a[i]);
	 getchar();
	 getchar();
	 return 0;
} 

下面是程序执行过程中数组 a 的变化过程,高亮的的数表示的是已归位的基准数。
6 1 2 7 9 3 4 5 10 8 原始
3 1 2 5 4 6 9 7 10 8
2 1 3 5 4 6 9 7 10 8
1 2 3 5 4 6 9 7 10 8
1 2 3 5 4 6 9 7 10 8
1 2 3 4 5 6 9 7 10 8
1 2 3 4 5 6 9 7 10 8
1 2 3 4 5 6 8 7 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10

参考资料:啊哈磊的《啊哈!算法》

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一粒程序米

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

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

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

打赏作者

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

抵扣说明:

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

余额充值