【入门】算法初步1 排序

目录

排序

1、选择排序

2、插入排序

 3、排序题与sort函数的应用

(1)、相关结构体的定义

(2)、cmp函数的编写

(3)排名的实现

   (4)例题


排序

1、选择排序

选择排序是最简单的排序算法之一,我们来主要学习一下简单选择排序。简单排序是指,对于一个序列A中的元素A[1]~A[n],令i从1到n枚举,进行n趟操作,每趟从待排序部分[ i,n ]中选择最小的元素,令其与待排序部分的第一个元素A[i]进行交换,这样元素A[i]就会与当前有序区间[ 1,i-1 ]形成新的有序区间[1,i],于是在进行了n趟操作后,所有的元素就会是有序的。如图:于是算法实现的逻辑就很明显了:总共需要进行n趟操作(1<=i<=n),每趟操作选出待排序部分[i,n]中最小的元素,令其与A[i]交换。因此总复杂度为O(n平方)。代码如下:

void selectSort()
{
	for (int i = 0; i <= n; i++)  //进行n趟操作
	{
		int k = i;
		for (int j = i; j <= n; j++) { //选出 [i,n]中最小的元素,下标为K
			if (A[j] < A[k])
				k = j;
		}
		int temp = A[i]; //交换A[k]与A[i]
		A[i] = A[k];
		A[k] = temp;
	}
}

2、插入排序

插入排序也是最简单的一类排序方法,本节主要介绍众多插入排序方法中最直观的直接排序。直接插入排序是指,对序列A的n个元素A[1]~A[n],令i从2到n枚举,进行n-1趟排序操作。假设某一趟,序列A的前i-1个元素A[1]~A[i-1]已经有序,而范围[i,n]还未有序,那么该趟从范围[1,i-1]中寻找某个位置j,使得A[i]插入位置j后(此时A[j]~A[i-1]会向后移一位至A[j+1]~A[i]),范围[1,i]有序。下面举一个列子来说明这个过程。

我们通过上面的例子,可以对直接插入排序有一个清晰的认知,下面请看代码:

int A[maxn], n;//n为元素个数,下标为1~n
void insertSort()
{
	for (int i = 2; i <= n; i++) {  //进行n-1趟排序
		int temp = A[i], j = i;  //temp临时存放A[i],j从i开始往前枚举
		while (j > 1 && temp < A[j - 1]) {//只要temp小于前一个元素A[j-1]
			A[j] = A[j - 1];  //把A[j-1]后移一位至A[j]
			j--;
		}
		A[j] = temp;//插入位置为j
	}
}

 3、排序题与sort函数的应用

由于考试中的排序题中大部分都只需要得到排序的最终结果,而不需要去写排序的完整过程,因此我们可以直接使用C语言中库函数qsort或者C++中的sort函数进行排序,这样我们就可以把更多心思放在逻辑上了。这里我就写简单的写一下,后面我会专门写一篇关于这个函数的。

(1)、相关结构体的定义

  对于排序题,有的会在题目中给出个体的许多信息,例如学生有姓名、准考证号、分数、排名等信息。这些信息在排序中一般都会用到,因此为了方便编写代码,常常将它们存至一个结构体当中

然后用结构体数组来表示多个个体。就以上面学生的例子来说,就可以定义结构体类型Student,用于存放信息。

struct Student {
    char name[10];//姓名
    char id[10];//准考证号
    int score;//分数
    int r;//排名
}stu[100010];

(2)、cmp函数的编写

我们在使用sort进行排序时,需要提供cmp函数实现的排序规则。这个我们就简单的说一下

以上段提到的要求为例,实际上需要完成的排序规则可以等价表述如下:

  1. 如果两个学生分数不相同,那么分数高的排在前面。
  2. 否则,将姓名字典序小的排在前面。

由此我们可以写出相对应的cmp函数

bool cmp(Student a, Student b)
{
    if (a.score != b.score) return a.score > b.score;
    else return strcmp(a.name, b.name) < 0;
}

这里的 strcmp 函数是string.h头文件下用来比较两个char型数组的字典序大小的。其中strcmp(str1,str2)当str1的字典序小于str2时返回一个负数,当等于时返回0,当大于时返回一个正数。

(3)排名的实现

很多排序题都会要求在排序之后计算出每个个体的排名,开且规则一般是:分数不同的,排名不同,分数相同的排名相同但占用一个排位。例如有五个学生的分数分别为90、88、88、88、86,那么这五位同学的排名分别是1、2、2、2、5。

一般都需要在结构体类型定义时就把排名这项加到结构体中(正如上题Student 炎型的定义一样)。于是在数组排序完成后就有下面两种万法来实现排名的计算。

①先将数组第一个个体(假设数组下标从0开始)的排名记为1,然后遇历剩余个体; 如果当前个体的分数等于上一个个体的分数,那么当前个体的排名等于上一个个体的排名,否则,当前个体的排名等于数组下标加1。对应的代码如下:

stu[0].r = 1;
for (int i = 1; 1 < n ; i++)
{
    if (stu[i].score == stu[i - 1].score) {
        stu[i].r = stu[i - 1].r;
    }
    else {
        stu[i].r = i + 1;
    }
}

②而有时题目中不一定需要真的把排名记录下来,而是直接输出即可,那么也可以用这样的办法:令 int 型变量初值为1,然后遍历所有个体:如果当前个体不是第一个个体且当前个体的分数不等于上一个个体的分数,那么令 r 等于数组下标加1,这时就是当前个体的排名,直接输出即可。这样的做法适用于需要输出的信息过多,导致第一种方法代码冗长的情况。

int r = 1;
for (int i = 0; i < n; i++) {
    if (i > 0 && stu[i].score != stu[i - 1].score) {
        r = i + 1;
    }
}

那下面我们来浅浅的来个例题吧!

(4)例题

题目:

有n个考场,每个考场有若干数量的考生。现在给出各个考场中考生的准考证号与分数,要求将所有考生按分数从高到低排序,并将顺序所有考生的准考证号、排名、考场号以及考场内的排名。

思路:

在结构体类型Student 中存放题目要求的信息(准考证号、分数、考场号以及考场内排名),根据题目要求,需要写一个排序函数cmp,规则如下;

①当分数不同时,技分数从大到小排序。
②否则,按准考证号从小到大排序。

bool cmp(Student a, Student b)
{
    if (a.score != b.score) return a.score > b.score;//先按分数从高到低排序
    else return strcmp(a.id, b.id) < 0; //若分数相同,则按准考证号从小到大排序
}

而算法要分为下面三个步骤:

步骤1:按考场读入各考生的信息,并对当前读入考场的所有考生进行排序,之后将该考场的所有考生的排名写入他们的结构体中。

步骤2:对所有考生进行排序。

步骤3:按顺序一边计算总排名,一边输出所有考生的信息。

代码如下

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct Student {
	char id[15];//准考证号
	int score;//分数
	int number;//考场号
	int rank;//考场内排名
}stu[10010];
bool cmp(Student a, Student b)
{
	if (a.score != b.score) return a.score > b.score;//先按分数从高到低排序
	else return strcmp(a.id, b.id) < 0; //若分数相同,则按准考证号从小到大排序
}
int main()
{
	int n, k, num = 0;//num为总考生数
	scanf("%d", &n);//n为考场数
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &k);  //考场内人数
		for (int j = 0; j < k; j++) {
			scanf("%s %d", stu[num].id, &stu[num].score);
			stu[num].number = i;   //该考生的考场号为i
			num++;              //总考生数加1
		}
		sort(stu + num - k, stu + num, cmp); //将该考场的考生排序
		stu[num - k].rank = 1;           //将该考场第一名rank记为1
		for (int j = num - k + 1; j < num; j++) {   //对该考场剩余的考生
			if (stu[j].score == stu[j - 1].score) {  //如果与前一位考生同分
				stu[j].rank = stu[j - 1].rank;//rank也相同
			}
			else {                       //如果与前一位考生不同分
				stu[j].rank = j + 1 - (num - k); //当前考生人数
			}
		}
	}
	printf("%d\n", num);   //输出总考生人数
	sort(stu, stu + num, cmp); //将所有考生排名
	int r = 1;       //当前考生的排名
	for (int i = 0; i < num; i++) {
		if (i > 0 && stu[i].score != stu[i - 1].score) {
			r = i + 1;     //当前考生与上一个考生分数不同时,让r更新人数
		}
		printf("%s ", stu[i].id);
		printf("%d %d %d\n", r, stu[i].number, stu[i].rank);
	}
	return 0;
}

 代码有点长,咱们慢慢理解。

大家应该也发现了,这里不止有C语言还有C++,下面我们还要学习算法咯!

路途很长,我们加油!

欢迎大家加入社区哟!

https://bbs.csdn.net/forums/Bit-runout?category=0https://bbs.csdn.net/forums/Bit-runout?category=0

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 35
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小白小白,一定发财。

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

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

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

打赏作者

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

抵扣说明:

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

余额充值