排序

最快最简单的排序---桶排序

简化版桶排序

例题:期末考试完了,老师要同学们的分数按照从高到低的顺序排序,班上只有五个同学,分别得分5、3、5、2、8分(满分10分)。

思想:

1. 我们需要申请一个大小为11的数组a[11],编号从a[0]~a[10]。刚开始时,我们把这11个变量全赋值为0,表示这些分数还都没有人得过。例如,a[0]=0表示目前还没有人得过0分;同理a[1]=0表示还没有人得过1分......

2. 下面开始处理每一个人的分数。第一个人的分数是5分,我们就将相应的a[5]的值在原来基础上增加1,即将a[5]的值从0改为1,表示5分出现过一次。第二个人的分数是3分,我们就将相应的a[3]的值在原来基础上增加1,即将a[3]的值从0改为1,表示3分出现过一次。注意啦,第三个人的分数也是五分,所以a[5]的值需要在1的基础上再增加1,表示5分出现两次。依次类推。

3. 你发现没有,a[0]~a[10]中的数值其实就是0分到10分每个分数出现的次数。接下来我们只需将出现过的分数打印出来就可以了,出现几次就打印几次。


相应代码:

#include<cstdio>
int main() {
	int a[11],n;
	for (int i = 1; i < 11; i++)
		a[i] = 0;//初始化为零
	for (int i = 1; i <= 5; i++) {
		scanf("%d", &n);//把每一个数读到变量t中
		a[n]++;//关键
	}
	for (int i = 1; i < 11; i++) {//关键
		for (int j = 1; j <= a[i]; j++) {//出现了几次就打印几次
			printf("%d ", i);
		}
	}
	system("pause");
	return 0;
}

代码实现:


例题2:

输入

10

8 100 50 22 15 6 1 1000 999 0

输出

1000 999 100 50 22 15 8 6 1 0

相应代码:

#include<cstdio>
int main() {
	int book[maxn], n, m, p;
	while (scanf("%d", &n) != EOF) {
		for (int i = 0; i < maxn; i++)
			book[i] = 0;
		for (int i = 1; i <= n; i++) {
			scanf("%d", &m);
			book[m]++;
		}
		for (int i = maxn - 1; i >= 0; i--)//从大到小输出
			for (int j = 1; j <= book[i]; j++)
				printf("%d ", i);
		printf("\n");
	}
	system("pause");
	return 0;
}


 时间复杂度O(n+m)。 

这只是一个简单的桶排序算法,真正的通排序算法要比这个更复杂。现在分别有5个人的名字和分数:aa 5、bb 3、cc 5、dd 2、nn 8,要求按照分数从高到低输出他们的名字。此题如果使用简单的桶排序算法仅仅是把分数进行了排序,对人本身没有进行排序。所以,就要运用另一种排序方法---冒泡排序。

邻居好说话---冒泡排序

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

例如需要将12 35 99 18 76这五个数按照从大到小排序,首先比较第一位与第二位的大小,12小于35两者交换,变为35 12 99 18 76;再比较第二位与第三位的大小,12与99比较前者小于后者,两者交换,变为35 99 12 18 76;依次类推,直到12(最小的排到最后)排到最后,第一趟结束,变为35 99 18 76 12。再进行第二趟、第三趟...直到按照从大到小的顺序排好为止。

总结:如果有n个数进行排序,只需将n-1个数归位,也就是说要进行n-1趟操作。

例题:

输入

10

8 100 50 22 15 6 1 1000 999 0

输出

1000 999 100 50 22 15 8 6 1 0

相应代码:

#include<cstdio>
int main() {
	int n, a[maxn];
	while (scanf("%d", &n) != EOF) {
		for (int i = 0; i < n; i++)
			scanf("%d", &a[i]);
		for (int i = 0; i < n-1; i++) {
			for (int j = 0; j < n-i-1; j++) {//关键一步,减少每一步的重复
				int temp;
				if (a[j] < a[j + 1]) {
					temp = a[j];
					a[j] = a[j + 1];
					a[j + 1] = temp;
				}
			}
		}
		for (int i = 0; i < n; i++)
			printf("%d ", a[i]);
		printf("\n\n");
	}
	system("pause");
	return 0;
}

桶排序解决不了的问题:

代码实现:



相应代码:

#include<cstdio>
struct student {
	char name[25];
	int score;
};//创建一个结构体用来存储姓名和分数

int main() {
	struct student a[100], t;
	int n,m;
	while (scanf("%d", &n) != EOF) {
		scanf("%d", &m);
		for (int i = 1; i <= m; i++)
			scanf("%s%d", &a[i].name, &a[i].score);
		//按分数从高到低进行排序
		for (int i = 1; i <= m - 1; i++) {
			for (int j = 1; j <= m - i; j++) {
				if (a[j].score < a[j + 1].score) {//整体互换
					t = a[j];
					a[j] = a[j + 1];
					a[j + 1] = t;
				}
			}
		}

		for (int i = 1; i <= m; i++)
			printf("%s\n", a[i].name);
		printf("\n");
	}
	system("pause");
	return 0;
}

冒泡排序的核心部分是双重嵌套循环。时间复杂度为O(n^2)。


3. 最常用的排序---快速排序

冒泡排序虽然解决了桶排序浪费空间的问题,但在算法的执行效率上牺牲了很多,而快速排序既不浪费空间又可以快一点。

思想:

首先要在要排序的数列中找一个数作为基准数。例如,对序列6 1 2 7 9 3 4 5 10 8进行排序,可将6作为基准数;然后,通过快速排序法将小于基准数6的数放在6的左边,大于基准数6的数放在6的右边;再运用快速排序函数分别将基准数左右的序列进行排序,知道排好为止。

注意,每次都是从右往左先开始移动,再从左往右移动寻找。

例题:

输入

10

6 1 2 7 9 3 4 5 10 8

输出

1 2 3 4 5 6 7 8 9 10

相应代码:

#include<cstdio>

int a[101], n;
void quicksort(int left, int right) {
	int t, temp, i, j;
	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[j];
			a[j] = a[i];
			a[i] = t;
		}
	}
	//最终将基准数归位
	a[left] = a[i];
	a[i] = temp;

	quicksort(left, i - 1);//继续处理左边的,这是一个递归的过程
	quicksort(i + 1, right);//继续处理右边的,这是一个递归的过程
	return;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);

	quicksort(1, n);

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

关键点是运用了函数的调用和递归。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值