news 2026/2/27 7:01:18

快速排序的理解与实践(c语言实现)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
快速排序的理解与实践(c语言实现)

快速排序的理解与实践

排序是计算机程序中常见的操作,而快速排序以其高效性成为许多程序员的优先选择。第一次接触快速排序时,我被它巧妙的分治思想所吸引——将一个大问题分解为若干小问题,逐个解决后再合并结果。这种思维方式不仅适用于排序,也适用于许多其他复杂问题的解决。

快速排序的基本思路其实很直观。想象一下你要整理书架上杂乱无章的书籍,一种有效的方法是先选择一本书作为参考,然后把所有比这本书标题字母顺序靠前的书放在左边,靠后的书放在右边。接着对左右两边的书籍分别重复这个过程,直到每个小堆都排好序。这就是快速排序的核心:选择一个基准,分割数据,然后递归处理。

在C语言中实现快速排序,我们首先需要理解几个关键步骤。算法的核心是分割函数,它负责将数组重新排列,使得基准元素左侧的所有元素都不大于基准,右侧的所有元素都不小于基准。这个函数通过两个指针在数组中移动,交换不符合条件的位置上的元素,直到整个数组被正确分割。

#include<stdio.h>#include<stdlib.h>#include<string.h>voidswap(int*a,int*b){intt=*a;*a=*b;*b=t;}intpart(intarr[],intl,inth){intp=arr[h];inti=l-1;for(intj=l;j<h;j++){if(arr[j]<p){i++;swap(&arr[i],&arr[j]);}}swap(&arr[i+1],&arr[h]);returni+1;}voidquick_sort(intarr[],intl,inth){if(l<h){intpi=part(arr,l,h);quick_sort(arr,l,pi-1);quick_sort(arr,pi+1,h);}}

上面的代码展示了快速排序的基本实现。我们定义了一个交换函数来交换两个整数的值,分割函数选择数组的最后一个元素作为基准,然后遍历数组将小于基准的元素移动到左侧。最后将基准元素放到正确位置,返回该位置的索引。快速排序函数则递归地对基准左右两侧的子数组进行排序。

虽然这个实现简单易懂,但它有一个潜在问题:当数组已经有序或接近有序时,选择最后一个元素作为基准会导致分割不均匀,从而降低算法效率。为了解决这个问题,我们可以采用随机选择基准的方法,这样即使在最坏情况下也能保持较好的平均性能。

实际编程中,我们通常不需要自己实现快速排序算法,因为C标准库提供了经过高度优化的qsort函数。这个函数的强大之处在于它的通用性——它可以对任何类型的数据进行排序,从简单的整数到复杂的结构体都可以处理。

qsort函数的使用需要理解几个关键参数:要排序的数组起始地址、元素个数、每个元素的大小,以及一个比较函数。比较函数是qsort的灵魂,它定义了排序的规则。这个函数接收两个指向待比较元素的指针,返回一个整数表示它们的大小关系:负数表示第一个元素小于第二个,零表示相等,正数表示第一个大于第二个。

让我们通过一个简单例子来理解qsort的使用。假设我们有一个整数数组需要排序,我们需要提供一个比较整数的函数,然后调用qsort即可完成排序。

intcmp_int(constvoid*a,constvoid*b){intx=*(constint*)a;inty=*(constint*)b;if(x<y)return-1;if(x>y)return1;return0;}intmain(){intnums[]={5,2,8,1,9,3};intn=sizeof(nums)/sizeof(nums[0]);qsort(nums,n,sizeof(int),cmp_int);for(inti=0;i<n;i++){printf("%d ",nums[i]);}return0;}

这个例子展示了如何使用qsort对整数数组排序。关键点在于比较函数必须正确处理三种情况:小于、等于和大于。对于整数,我们可以直接相减,但直接返回差值可能导致溢出,所以更安全的做法是使用条件判断。

qsort的真正威力体现在对复杂数据类型的排序上,特别是结构体。在实际应用中,我们经常需要根据结构体的某个或某些字段进行排序。通过编写不同的比较函数,我们可以轻松实现各种排序需求。

假设我们有一个学生结构体,包含学号、姓名、成绩和年龄等信息。我们需要根据不同的标准对这些学生进行排序,比如按成绩从高到低、按年龄从小到大,或者按姓名字母顺序。

typedefstruct{intid;charname[50];floatscore;intage;}Stu;intcmp_stu_score(constvoid*a,constvoid*b){constStu*s1=(constStu*)a;constStu*s2=(constStu*)b;if(s1->score>s2->score)return-1;if(s1->score<s2->score)return1;return0;}intcmp_stu_age(constvoid*a,constvoid*b){constStu*s1=(constStu*)a;constStu*s2=(constStu*)b;returns1->age-s2->age;}intcmp_stu_name(constvoid*a,constvoid*b){constStu*s1=(constStu*)a;constStu*s2=(constStu*)b;returnstrcmp(s1->name,s2->name);}

这些比较函数分别实现了按成绩降序、按年龄升序和按姓名升序的排序规则。注意在比较成绩时我们返回-1和1而不是差值,因为浮点数的减法可能产生精度问题。字符串比较则使用标准库的strcmp函数,它已经正确处理了字母顺序。

有时候我们需要进行多级排序,比如先按成绩排序,成绩相同的再按姓名排序。这种情况下,我们可以在比较函数中先比较主要字段,如果相等再比较次要字段。

intcmp_stu_score_name(constvoid*a,constvoid*b){constStu*s1=(constStu*)a;constStu*s2=(constStu*)b;if(s1->score>s2->score)return-1;if(s1->score<s2->score)return1;returnstrcmp(s1->name,s2->name);}

这个比较函数首先比较成绩,如果成绩不同则按成绩排序;如果成绩相同,则使用strcmp比较姓名。多级排序在实际应用中很常见,比如成绩管理系统、员工数据库等。

对于包含指针的结构体或指针数组,qsort同样适用。有时候我们不想移动整个结构体(特别是结构体很大时),而是排序指向结构体的指针数组。这种情况下,比较函数需要解引用两次指针。

intcmp_stu_ptr_score(constvoid*a,constvoid*b){constStu**s1=(constStu**)a;constStu**s2=(constStu**)b;if((*s1)->score>(*s2)->score)return-1;if((*s1)->score<(*s2)->score)return1;return0;}

这个比较函数用于排序指向Stu结构体的指针数组。注意参数类型是指向指针的指针,所以我们需要先解引用得到Stu指针,再访问结构体成员。

使用qsort时需要注意一些细节。比较函数必须遵守特定的约定:当第一个参数应该排在第二个参数之前时返回负数,之后时返回正数,相等时返回零。不遵守这个约定会导致排序结果不正确。另外,对于浮点数的比较要小心处理精度问题,对于字符串比较要注意大小写敏感性(strcmp区分大小写,如果需要不区分大小写可以使用strcasecmp)。

在实际应用中,我们还需要考虑排序的稳定性。快速排序不是稳定排序,即相等元素的相对位置在排序后可能会改变。如果需要稳定排序,可以考虑使用归并排序等其他算法,或者给每个元素添加一个原始序号的字段,在比较函数中作为最后的比较依据。

对于初学者来说,理解快速排序的最好方法是从简单例子开始,逐步增加复杂度。可以先理解整数数组的排序,然后尝试结构体排序,最后考虑多级排序和指针数组排序。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/21 7:00:38

8、深入了解Bash:功能、安装与使用指南

深入了解Bash:功能、安装与使用指南 1. 引言 Bash(GNU Bourne Again Shell)是GNU项目的shell,基于Bourne shell(sh)开发。它融合了c shell(csh)、tc shell(tcsh)和Korn shell(ksh)的特性,与sh差异较小,多数sh脚本可在Bash中直接运行。Bash由Brian Fox编写,目前…

作者头像 李华
网站建设 2026/2/8 3:32:54

Open-AutoGLM 实战:手把手教你用 AI 做App自动化测试「喂饭教程」

Open-AutoGLM 实战&#xff1a;手把手教你用 AI 做App自动化测试「喂饭教程」前言开始之前的几点说明准备工作第一步&#xff1a;Python 环境第二步&#xff1a;安装 ADB 工具第三步&#xff1a;准备你的 Android 手机快速部署&#xff1a;10 分钟搞定克隆项目到本地创建独立的…

作者头像 李华
网站建设 2026/2/21 21:18:37

存储引擎内核:深入解析 LSM-Tree 原理与高吞吐写入实践

【精选优质专栏推荐】 《AI 技术前沿》 —— 紧跟 AI 最新趋势与应用《网络安全新手快速入门(附漏洞挖掘案例)》 —— 零基础安全入门必看《BurpSuite 入门教程(附实战图文)》 —— 渗透测试必备工具详解《网安渗透工具使用教程(全)》 —— 一站式工具手册《CTF 新手入门实战教…

作者头像 李华
网站建设 2026/2/21 6:09:44

保姆级教程:iPhone 某人短信消失?9 种解决方法,小白也会用

当某个联系人的短信突然从你的 iPhone 上消失时&#xff0c;你会感到很沮丧。你知道你没有删除它们&#xff0c;但整个对话却神秘地消失了。你并不孤单。许多 iPhone 用户在论坛上都报告了这个问题。无论是 iOS 故障、同步问题还是意外删除&#xff0c;本文都会从各个角度进行分…

作者头像 李华
网站建设 2026/2/17 19:06:44

31、进程间通信:信号、管道与套接字详解

进程间通信:信号、管道与套接字详解 1. 信号设置与处理 信号是进程间通信的重要方式之一,在处理信号时,我们可以设置不同的信号行为。以下是信号行为设置的相关模式: | 操作 | System V 模式 | POSIX 模式 | | — | — | — | | 忽略信号 | sigaction(signo,new,old) …

作者头像 李华