算法笔记之第四章 排序

选择排序

i i 1 1 n n 枚举,进行 n n 趟操作,每趟从待排序部分 [ i , n ] [i,n] 中选择最小的元素,令其与待排序部分的第一个元素 A [ i ] A[i] 进行交换,这样 A [ i ] A[i] 就会与当前有序区间 [ 1 , i 1 ] [1,i-1] 形成新的有序区间 [ 1 , i ] [1,i]

一共 n n 趟操作,总复杂度为 O ( n 2 ) O(n^2)
在这里插入图片描述

void selectSort() {
    for(int i = 1; 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;
       }
  }  

插入排序

后面的元素不断有序地插入前面排好的元素(从后往前枚举已有序部分来确定插入位置)

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临时存放,j从i开始往前枚举
        while(j > 1 && temp < A[j - 1]) {//只要temp小于前一个元素
            A[j] = A[j - 1];//把A[j-1]往后移(空出位置)
            j--;
        }
        A[j] = temp;//插入位置为j
    }
}

sort函数(第六章)

(1)如何使用sort
必须加上头文件“#include <algorithm>”和“using namespace std;”

使用方法:

sort(首元素地址(必填),尾元素地址的下一个地址(必填),比较函数(非必填));

不写比较函数,则默认对前面给出的区间进行递增排序

#include <stdio.h>
#include <algorithm>
using namespace std;
int main(){
    int a[6] = {9, 4, 2, 5, 6, -1};
    //将a[0]~a[3]从小到大排序
    sort(a, a + 4);
    for(int i = 0; i < 6; i++) {
        printf("%d ", a[i]);
    }
    //将a[0]~a[5]从小到大排序
    sort(a, a + 6);
    for(int i = 0; i < 6; i++) {
        printf("%d ", a[i]);
    }
    return 0;
}    

也可以对double,char型数组排序(字典序)

如果想从大到小:

bool cmp(int a, int b){
    return a > b; //可以理解为当a > b时把a放在b前面
    }
    int main() {
        int a[] = {3, 1, 4, 2};
        sort(a, a + 4, cmp);
        for(int i = 0; i < 4; i++) {
            printf("%d ", a[i]); //输出 4 3 2 1
        }
        return 0;
 }

也可以对double,char型这样操作

记住:cmp里从小到大用“<”,因为a<b就是左小右大,反之。

(2)结构体数组的排序

定义了如下结构体

struct node{
    int x, y;
}ssd[10];

如果想对ssd数组从大到小排

bool cmp(node a, node b) {
    return a.x>b.x;

例子

#include <stdio.h>
#include <algorithm>
using namespace std;
struct node{
    int x, y;
}ssd[10];
bool cmp(node a, node b) {
    return a.x > b.x; //按x值从大到小对结构体数组排序
}
int main() {
   ssd[0].x = 2; //{2, 2}
   ssd[0].y = 2; 
   ssd[1].x = 1; //{1, 3}
   ssd[1].y = 3;     
   sort(ssd,ssd + 2, cmp); //排序
   for(int i = 0; i < 3; i++){
       printf("%d %d\n", ssd[i].x, ssd[i].y);
   }
   return 0;
}

如果先按x从大到小排序,但当x相等的情况下,按照y的大小从小到大排序(二级排序),cmp的写法是:

bool cmp(node a, node b) {
    if(a.x != b.x) return a.x > b.x;
    else return a.y < b.y;
}

(3)容器的排序(跳过)

回本章(1)相关结构体的定义

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

(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;
}

(3)排名的实现

分数不同的排名不同,分数相同的排名相同但占用同一个排位。
遍历个体,如果每个个体分数等于上一个个体,则当前个体排名等于上一个个体排名,否则当前个体排名等于数组下标加一。

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

有时不一定要把排名记录下来,直接输出即可,避免代码冗长

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

例题

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

思路

在结构体类型student中存放准考证号、分数、考场号以及考场内排名。写一个排序函数cmp,规则为:
1 当分数不同时,按分数从大到小排
2 否则按准考证号从小到大排

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 按排序一边计算总排名,一边输出考生信息

注意同一考场考生单独排序方法:定义int型变量num,用来存放当前获取到的考生人数。每读入一个考生信息,就让num自增。读取完后考生对应的数组下标为[num-k,num)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct Student {
    // 准考证号,分数,考场号,考场内排名
    char id[15];
    int score;
    int location_number;
    int local_rank;
}stu[30010];
bool cmp(Student a, Student b) {
    //先按分数从高到低,否则按1准考证大小
    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].location_number = i; // 该考生的考场号为i
             num++; // 总考生数加1
         }
         sort(stu + num - k, stu + num, cmp); // 将该考场的考生排序
         stu[num - k].local_rank = 1; // 该考场第一名的locan_rank记为1
         for(int j = num - k + 1; j < num; j++) //对该考场剩余的考生
             if(stu[j].score == stu[j - 1].score) { //如果与前一位考生同分
             //local_rank也相同
             stu[j].local_rank = stu[j - 1].local_rank;
         } else {
             // local_rank 为该考生前的人数
             stu[j].local_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更新为人数+1
        }
        printf("%s ",stu[i].id);
        printf("%d %d %d\n", r, stu[i].location_number, stu[i].local_rank);
    }
    return 0;
}    

猜你喜欢

转载自blog.csdn.net/weixin_44770248/article/details/89641668