BlockLune's Blog

首页 标签 关于 |

C 标准库中 qsort 和 C++ STL 中 sort 的用法

发表于 2022-12-08
更新于 365 日前 许可证 CC BY-NC-SA 4.0 ccppalgorithmprogramming-languagesorting

虽然到现在还是不能完全理解 qsortsort 这两个函数的底层原理,但至少,先学会如何使用吧。

qsort

需要包含的库:

stdlib.h (C++ 中 是 cstdlib)

函数原型:

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *))

参数解释:

  • base: 必选,数组名(数组首元素的地址);
  • nitems: 必选,数组中元素的个数;
  • size: 必选,数组中单个元素的大小;
  • compar: 必选,一个函数指针,具体这个函数要干嘛下边细说。

最后一个参数是函数指针,这个指针指向的函数的原型应该类似于下边这样:

int cmp(const void *a, const void *b);

这是一个返回值为 -101 的函数。

如果要实现升序,那么应该是:

a>b:返回 1(或其他正数); a==b:返回 0; a<b: 返回 -1(或其他负数);

如果是降序,那么就应该反过来,像下边这样:

a>b:返回 -1(或其他负数); a==b:返回 0; a<b: 返回 1(或其他正数);

比如我要对 int 类型的数组升序排序,那么我的 cmp 函数应该像下边这样:

int cmp(const void *a, const void *b)
{
    const int *x = (const int *)a;
    const int *y = (const int *)b;
    if (*x > *y)
        return 1;
    else if (*x == *y)
        return 0;
    else
        return -1;
}

或者也可以简化成下边这样:

int cmp(const void *a, const void *b)
{
    return (*(const int *)a - *(const int *)b);
}

这里的 a>b 等比较方式只是形式上我这么写,实际上有可能这两个元素我并不能直接这么比(比如如果这里的 a、b 都是 struct),那么就应该类似下边这样:

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    unsigned int ID;
    unsigned int score;
} STUDENT;

int cmp(const void *, const void *);

int main()
{
    STUDENT stu[5] = {{1, 5}, {2, 3}, {3, 1}, {4, 2}, {5, 4}};
    qsort(stu, 5, sizeof(STUDENT), cmp);
    for (int i = 0; i < 5; i++)
    {
        printf("ID: %u, Score: %u\n", stu[i].ID, stu[i].score);
    }
    return 0;
}

int cmp(const void *a, const void *b)
{
    const STUDENT *x = (const STUDENT *)a;
    const STUDENT *y = (const STUDENT *)b;
    // 降序排序
    if (x->score > y->score)
        return -1;
    else if (x->score == y->score)
        return 0;
    else
        return 1;
}

输出是:

ID: 1, Score: 5
ID: 5, Score: 4
ID: 2, Score: 3
ID: 4, Score: 2
ID: 3, Score: 1

sort

需要包含的库:

algorithm(C++ STL 中的算法库)

函数原型:

template <class RandomAccessIterator, class Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);

参数解释:

first: 必选,排序开始处(参与排序的第一个元素); last: 必选,排序结束处的后一个紧挨着的位置(参与排序的最后一个元素的后一个位置); comp: 可选,用来指定怎么排序的函数,没有的话如果可以默认升序。

下边给出一些例子:

给一个数组升序排序:

int arr[5] = {5, 1, 3, 2, 4};
sort(arr, arr + 5);

给一个 vector 降序排序:

vector<int> nums = {5, 1, 3, 2, 4};
sort(nums.begin(), nums.end(), greater<int>());

这里用了 greater<typename>() 这个东东直接表达我这个排序需要降序排序,类似的还有 less<typename>() 用来指定升序。

然后是自定义这第三个参数,我们就用上边 qsort 那个例子吧:

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

struct STUDENT
{
    unsigned int ID;
    unsigned int score;
};

bool cmp(STUDENT, STUDENT);

int main()
{
    STUDENT stu[5] = {{1, 5}, {2, 3}, {3, 1}, {4, 2}, {5, 4}};
    sort(stu, stu + 5, cmp);
    for (int i = 0; i < 5; i++)
    {
        printf("ID: %u, Score: %u\n", stu[i].ID, stu[i].score);
    }
    return 0;
}

bool cmp(STUDENT a, STUDENT b)
{
    if (a.score > b.score)
        return true;
    else
        return false;
}

或者也可以把 cmp 写成下边这种更容易记住的方式:

bool cmp(STUDENT a, STUDENT b)
{
    return a.score > b.score;
    // 表示排序完成后前一个元素比后一个元素大,即降序排序
}

输出和上边是一样的。

Important

在 qsort 中,最后一个参数的函数的返回值应是一个有符号整型。在期望 升序 排序时,这个返回值应该指定为:第一个元素大于第二个元素,返回正数; 第一个元素等于第二个元素,返回 0;第一个元素小于第二个元素,返回负数。

在 C++ STL 的 sort 中,最后一个参数的返回值应是一个布尔值。在期望 升序 排序时,这个返回值应该是 (第一个元素 < 第二个元素) 的运算结果。