c语言中的排序算法有哪些 qsort函数如何使用

来源:Vuejs社区作者:徐致远头衔:网络博主
导读:本期聚焦于徐致远创作的《c语言中的排序算法有哪些 qsort函数如何使用》,敬请观看详情。C语言中实现排序有多种方式,常见的算法包括冒泡排序、选择排序、插入排序、快速排序、归并排序和堆排序等,它们各自的时间复杂度和适用场景差别很大。本文先介绍几种经典排序算法的原理与C语言实现,再重点讲解标准库中qsort函数的用法,包括compare比较函数的编写规范、对int数组、结构体数组、字符串数组的排序示例,以及使用qsort时容易踩到的坑。看完这篇内容,你可以根据数据规模选择合适的排序算法,并熟练用qsort完成各种自定义排序。

C语言没有像C++那样内置sort这样的模板函数,但标准库提供了qsort,配合自己实现的经典排序算法,基本能覆盖日常开发中的所有排序需求。本文先梳理几种常见的排序算法,再详细讲解qsort函数的正确用法,包括比较函数的写法和一些容易出错的地方。

c语言中的排序算法有哪些 qsort函数如何使用

C语言中常见的排序算法有哪些

按照时间复杂度,排序算法大致分为两类:简单排序和高级排序。简单排序包括冒泡排序、选择排序和插入排序,平均时间复杂度都是O(n²),适合数据量小或者基本有序的场景;高级排序包括快速排序、归并排序、堆排序和希尔排序,平均时间复杂度可以达到O(n log n),适合大数据量的场合。

冒泡排序是最容易理解的一种,它重复地走访数组,比较相邻两个元素,如果顺序错误就交换。整个过程像水底的气泡一样往上浮。选择排序则是每一轮从未排序部分中挑出最小(或最大)的元素,放到已排序部分的末尾。插入排序把数组分成已排序和未排序两部分,每次从未排序部分取一个元素,在已排序部分中找到合适的位置插入,对于基本有序的数组它的效率非常高,接近O(n)。

三种简单排序的C语言实现

下面给出冒泡排序和插入排序的代码实现,方便对照理解。冒泡排序还可以加一个交换标志,如果某一轮没有发生交换,说明数组已经有序,可以直接退出,这是一个常用的优化手段。

// 冒泡排序(带提前退出优化)
void bubble_sort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int swapped = 0;
        for (int j = 0; j < n - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                swapped = 1;
            }
        }
        if (!swapped) break;  // 本轮没有交换,说明已经有序
    }
}

// 插入排序
void insertion_sort(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];  // 后移腾出位置
            j--;
        }
        arr[j + 1] = key;
    }
}</code>

这两种排序都属于原地排序,只需要O(1)的额外空间。实际项目中如果数据规模在几百以内,插入排序的性能甚至不输快速排序,因为它的常数因子小,且没有递归调用的开销。这也是很多高级排序算法在分段较小时切换为插入排序的原因。

qsort函数怎么使用

qsort是C标准库中提供的通用排序函数,函数原型为void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))。四个参数分别是:数组首地址、元素个数、每个元素的字节数、比较函数的指针。

比较函数是qsort的核心,它接收两个指向待比较元素的指针,返回值规则为:返回负数表示第一个元素应排在前面,返回0表示相等,返回正数表示第一个元素应排在后面。需要注意的是,比较函数接收的是const void *类型指针,必须先强制转换成实际的元素类型指针,再解引用取值。

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

// 升序比较函数
int cmp_int(const void *a, const void *b) {
    int x = *(const int *)a;
    int y = *(const int *)b;
    return (x > y) - (x < y);  // 避免溢出的写法
}

int main() {
    int arr[] = {5, 2, 9, 1, 7, 3};
    int n = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, n, sizeof(int), cmp_int);
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    return 0;
}

有人喜欢写成return x - y;,这种写法在int存在溢出风险,比如x是很大的正数而y是很大的负数时,相减会溢出,导致排序结果错乱。推荐使用上面代码中(x > y) - (x < y)的写法,永远安全。另外qsort没有返回值,排序结果直接反映在原数组上。

用qsort对结构体和字符串排序

qsort真正的强大之处在于可以排序任意类型的数据,只要比较函数写对即可。比如有一个学生结构体数组,需要按成绩从高到低排序,成绩相同时按学号升序,可以这样写:

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

typedef struct {
    int id;
    char name[32];
    int score;
} Student;

// 按成绩降序,成绩相同按学号升序
int cmp_student(const void *a, const void *b) {
    const Student *sa = (const Student *)a;
    const Student *sb = (const Student *)b;
    if (sa->score != sb->score) {
        return sb->score - sa->score;
    }
    return sa->id - sb->id;
}

int main() {
    Student stus[] = {
        {101, "zhang", 85},
        {102, "li", 92},
        {103, "wang", 85}
    };
    qsort(stus, 3, sizeof(Student), cmp_student);
    for (int i = 0; i < 3; i++) {
        printf("%d %s %d\n", stus[i].id, stus[i].name, stus[i].score);
    }
    return 0;
}

对字符串数组排序时要注意一个细节:数组元素是char *指针时,比较函数里拿到的实际上是char **,需要两次解引用才能拿到字符串本身,然后用strcmp比较:

int cmp_str(const void *a, const void *b) {
    // 元素本身是char*,所以要先转成char**再解引用
    const char *sa = *(const char **)a;
    const char *sb = *(const char **)b;
    return strcmp(sa, sb);
}

这个地方是初学者最容易出错的位置,如果直接把const void *转成const char *再去strcmp,程序很可能直接崩溃,因为指针的层级错了。

使用qsort的注意事项与算法选择建议

第一,标准只规定qsort的行为是排序,具体用什么算法由实现决定,大多数实现采用快速排序的变体,因此qsort不保证稳定,相等元素的相对顺序可能改变。如果业务需要稳定排序,比如排序后还要保留同分学生的原始顺序,就需要改用归并排序或者自己在比较函数中加入额外字段。第二,比较函数必须满足严格弱序,即对相等元素必须返回0,如果写成永远返回正数或负数,可能导致越界访问甚至死循环。第三,传入的size参数必须和元素实际大小一致,排序结构体时写错sizeof会导致整个数组被错误切分。

在算法选择上,数据量小(比如一千以内)时手写插入排序完全够用;数据量大且类型固定时,qsort通用性好但因为有函数指针调用,每次比较都要经过间接跳转,性能比手写的快速排序略慢,对性能极其敏感的场景可以自己实现;如果是嵌入式环境,还要考虑qsort递归实现可能带来的栈空间消耗。理解了这些原理和细节,就能在C语言项目中灵活应对各种排序需求了。

C语言排序算法qsort函数快速排序修改时间:2026-08-31 23:15:05

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。