一、qsort 函数的基本介绍
qsort 是 C 标准库中的一个快速排序函数,位于 <stdlib.h> 头文件中。它的原型如下:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
base:指向待排序数组的首元素指针。nmemb:数组中元素的数量。size:每个元素的大小(以字节为单位)。compar:比较函数的指针,用于决定排序顺序。
二、比较函数的编写
比较函数 compar 的返回值决定了排序顺序:
- 返回负数:第一个参数应排在第二个参数之前。
- 返回零:两参数相等。
- 返回正数:第一个参数应排在第二个参数之后。
比较函数示例(升序排序整型数组):
int compare_ints(const void *a, const void *b) {
int x = *(const int *)a;
int y = *(const int *)b;
return (x > y) - (x < y);
}
三、降序排序的实现
只需在比较函数中反转返回值:
int compare_ints_desc(const void *a, const void *b) {
return (*(int *)b - *(int *)a);
}
四、性能与稳定性
qsort 平均时间复杂度为 O(n log n),但不保证稳定性(相等元素的相对顺序可能改变)。如需稳定排序,需使用其他算法。
五、常见数据类型的排序示例
1、整型数组排序
#include <stdio.h>
#include <stdlib.h>
int compare_ints(const void *a, const void *b) {
return (*(int *)a - *(int *)b);
}
int main() {
int arr[] = {5, 2, 8, 1, 4};
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(int), compare_ints);
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);
return 0;
}
1.1、其他排序规则
需要实现一个整型数组的排序,排序规则为:对于任意两个数字 a 和 b,比较 ab(正向拼接)和 ba(反向拼接)的值,若 ab < ba,则 a 应排在 b 前面。 最后整个数组可拼接成最大值。
- 自定义比较函数:利用
qsort函数的自定义比较功能,定义拼接后的字符串比较逻辑。 - 拼接字符串处理:将两个整数转换为字符串并拼接,比较拼接后的字符串大小。
- 避免数值溢出:直接拼接字符串而非转换为数值,避免大数溢出问题。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 自定义比较函数
int compare(const void *a, const void *b) {
char str_ab[24], str_ba[24]; // 假设数字不超过12位(避免溢出)
// 将a和b转换为字符串
sprintf(str_ab, "%d%d", *(int *)a, *(int *)b);
sprintf(str_ba, "%d%d", *(int *)b, *(int *)a);
// 比较拼接后的字符串
return strcmp(str_ab, str_ba);
}
// 示例调用
int main() {
int arr[] = {3, 30, 34, 5, 9};
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(int), compare);
// 输出排序结果
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
-
比较函数
compare:- 将两个整数
a和b分别拼接为ab和ba的字符串形式。 - 使用
strcmp比较两个字符串的字典序,返回结果作为排序依据。
- 将两个整数
-
主函数:
- 定义测试数组
arr,调用qsort进行排序。 - 排序后的数组按规则输出,例如输入
{3, 30, 34, 5, 9},输出为3 30 34 5 9(因为330 < 303,334 < 343等)。
- 定义测试数组
注意事项
- 字符串缓冲区大小:假设每个数字不超过12位,拼接后的字符串长度不超过24位。若数字更大,需调整缓冲区大小。
- 性能优化:频繁的字符串拼接可能影响性能,若数据量较大,可考虑优化拼接方式或缓存结果。
2、字符串数组排序
2.1、使用指针数组定义字符串数组
指针数组方式适合存储固定数量的字符串,每个元素是指向字符串的指针。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
int main() {
const char *arr[] = {"banana", "apple", "cherry"};
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(const char *), compare);
for (int i = 0; i < n; i++) printf("%s\n", arr[i]);
return 0;
}
- 关键点:比较函数需解引用两层指针(
*(const char **)),因为qsort传递的是数组元素的地址(即char **类型)。
2.2、使用二维字符数组定义
二维数组方式适合存储长度固定的字符串,内存连续但可能浪费空间。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare(const void *a, const void *b) {
return strcmp((const char *)a, (const char *)b);
}
int main() {
char arr[][10] = {"banana", "apple", "cherry"};
int n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(arr[0]), compare);
for (int i = 0; i < n; i++) printf("%s\n", arr[i]);
return 0;
}
- 关键点:比较函数直接强转为
const char *,因为二维数组的每个元素是字符数组(char[10]),传递给qsort的是其首地址。
2.3、动态字符串数组(指针 + 动态内存)
动态分配方式灵活,适合运行时确定字符串数量和内容。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
int main() {
char **arr = malloc(3 * sizeof(char *));
arr[0] = strdup("banana"); // 动态分配并复制字符串
arr[1] = strdup("apple");
arr[2] = strdup("cherry");
int n = 3;
qsort(arr, n, sizeof(char *), compare);
for (int i = 0; i < n; i++) {
printf("%s\n", arr[i]);
free(arr[i]); // 释放每个字符串
}
free(arr); // 释放指针数组
return 0;
}
- 关键点:动态分配需管理内存(
malloc/free),比较函数与指针数组方式一致。使用strdup简化字符串复制(需手动释放)。
总结
- 指针数组:适合静态字符串集合,比较函数需解引用两层指针。
- 二维数组:适合固定长度字符串,内存连续但不够灵活。
- 动态分配:灵活性强,需注意内存管理。
根据实际需求选择合适的方式,注意比较函数和内存管理的差异。
3、结构体排序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[50];
int age;
} Person;
int compare_persons_by_age(const void *a, const void *b) {
return ((Person *)a)->age - ((Person *)b)->age;
}
int main() {
Person people[] = {{"Alice", 25}, {"Bob", 20}, {"Charlie", 30}};
int n = sizeof(people) / sizeof(people[0]);
qsort(people, n, sizeof(Person), compare_persons_by_age);
for (int i = 0; i < n; i++)
printf("%s: %d\n", people[i].name, people[i].age);
return 0;
}
4、浮点数排序-注意事项
由于浮点数精度问题,直接相减可能导致错误。建议使用以下方式:
int compare_floats(const void *a, const void *b) {
float x = *(const float *)a;
float y = *(const float *)b;
return (x > y) - (x < y);
}
5、二维数组排序
以下使用 C 语言的 qsort 函数对二维数组进行多种排序方式的实现。假设二维数组为 int arr[M][N],其中 M 为行数,N 为列数。
5.1、按某列排序(全体行参与排序)
int compare_col(const void *a, const void *b, int col) {
int *row1 = (int *)a;
int *row2 = (int *)b;
return (row1[col] > row2[col]) - (row1[col] < row2[col]);
}
// 按第 k 列升序排序
qsort(arr, M, sizeof(arr[0]),
(int (*)(const void *, const void *))compare_col);
5.2、按某行排序(全体列参与排序)
int compare_row(const void *a, const void *b, int row) {
int *col1 = (int *)a;
int *col2 = (int *)b;
return (arr[row][*col1] > arr[row][*col2]) -
(arr[row][*col1] < arr[row][*col2]);
}
// 按第 k 行升序排序(需额外处理)
int indices[N];
for (int i = 0; i < N; i++) indices[i] = i;
qsort(indices, N, sizeof(int),
(int (*)(const void *, const void *))compare_row);
也可行列互换后排序,再行列互换。
5.3、仅对某列排序(其他列不参与)
// 需将目标列提取为单独数组
int target_col[M];
for (int i = 0; i < M; i++) target_col[i] = arr[i][k];
qsort(target_col, M, sizeof(int), compare_int);
// 再写回原数组
for (int i = 0; i < M; i++) arr[i][k] = target_col[i];
5.4、全体元素排序(展平为一维)
int compare_all(const void *a, const void *b) {
return (*(int *)a > *(int *)b) - (*(int *)a < *(int *)b);
}
qsort(&arr[0][0], M * N, sizeof(int), compare_all);
5.5、多条件排序(第一列升序,第二列降序)
int compare_multi(const void *a, const void *b) {
int *row1 = (int *)a;
int *row2 = (int *)b;
if (row1[0] != row2[0])
return (row1[0] > row2[0]) - (row1[0] < row2[0]);
else
return (row2[1] > row1[1]) - (row2[1] < row1[1]);
}
qsort(arr, M, sizeof(arr[0]), compare_multi);
注意事项
- 对于按行排序和部分列排序,通常需要额外处理索引或数据提取
- 多条件排序时,比较函数需正确处理主次条件
- C 语言中二维数组的内存布局是连续的,可直接用
&arr[0][0]访问全体元素
【免责声明:本文代码来自AI生成,可能有误,请自行判断,别骂我骂AI谢谢:-)】

2712

被折叠的 条评论
为什么被折叠?



