【编程珠玑】【第一章】快速排序qsort的使用及实现
qsort包含在<stdlib.h>头文件中,此函数根据你给的比较条件进行快速排序,通过指针移动实现排序。排序之后的结果仍然放在原数组中。使用qsort函数必须自己写一个比较函数。
函数原型:
void qsort ( void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void * ) );
用法以及参数说明:
Sorts the num elements of the array pointed by base, each element size bytes long, using the comparator function to determine the order.
The sorting algorithm used by this function compares pairs of values by calling the specified comparator function with two pointers to elements of the array.
The function does not return any value, but modifies the content of the array pointed by base reordering its elements to the newly sorted order.
1. base Pointer to the first element of the array to be sorted.(数组起始地址,指向要排序的数组的第一个元素的指针。)
2. num Number of elements in the array pointed by base.(由 base 指向的数组中元素的个数。)
3. size Size in bytes of each element in the array.(数组中每个元素的大小,以字节为单位。)
4. comparator Function that compares two elements.(函数指针,用来比较两个元素的函数)
1、The function must accept two parameters that are pointers to elements, type-casted as void*. These parameters should be cast back to some data type and be compared.
2、The return value of this function should represent whether elem1 is considered less than, equal to, or greater than elem2 by returning, respectively, a negative value, zero or a positive value.(0,1,-1)
5. Return Value none (该函数不返回任何值。)
下面的实例演示了 qsort() 函数的用法。
一、对int类型数组排序
int num[100];
int cmp ( const void *a , const void *b ){
return *(int *)a > *(int *)b ? 1 : -1;
//return *(int *)a - *(int *)b; //升序排序
//return *(int *)b - *(int *)a; //降序排序
}
qsort(num,100,sizeof(num[0]),cmp);
二、对char类型数组排序(同int类型)
char word[100]; int cmp( const void *a , const void *b ){ return *(char *)a - *(char *)b; //return *(char *)a - *(char *)b; //升序排序 //return *(char *)b - *(char *)a; //降序排序 } qsort(word,100,sizeof(word[0]),cmp); int main(){ scanf("%s",word); n=strlen(word); qsort(word,n,sizeof(word[0]),cmp); printf("%s",word); return(0); }
三、对double类型数组排序
double in[100]; int cmp( const void *a , const void *b ){ return *(double *)a > *(double *)b ? 1 : -1; } qsort(in,100,sizeof(in[0]),cmp);
四、对结构体一级排序
struct Sample{ double data; int other; }s[100] //按照data的值从小到大将结构体排序 int cmp( const void *a ,const void *b){ return (*(Sample *)a).data > (*(Sample *)b).data ? 1 : -1; } qsort(s,100,sizeof(s[0]),cmp);
五、对结构体二级排序
struct Sample{ int x; int y; }s[100]; //按照x从小到大排序,当x相等时按照y从大到小排序 int cmp( const void *a , const void *b ){ struct Sample *c = (Sample *)a; struct Sample *d = (Sample *)b; if(c->x != d->x) return c->x - d->x; else return d->y - c->y; } qsort(s,100,sizeof(s[0]),cmp);
六、对结构体按照内部字符串进行排序
struct Sample{ int data; char str[100]; }s[100]; //按照结构体中字符串str的字典顺序排序 int cmp ( const void *a , const void *b ){ return strcmp( (*(Sample *)a).str , (*(Sample *)b).str ); } qsort(s,100,sizeof(s[0]),cmp);
附一些完整点的示例:
No.1 对字符串二维数组排序
#include <stdio.h> #include <stdlib.h> #include <string.h> char s[2001][1001]; int cmp(const void *a, const void *b){ return strcmp((char *)a,(char *)b); } int main(){ int i,n; scanf("%d",&n); getchar(); for(i=0;i<n;i++) gets(s[i]); qsort(s,n,1001*sizeof(char),cmp); for(i=0;i<n;i++) puts(s[i]); return 0; }
No.2.最常见的,对int数组排序
#include <stdio.h> #include <string.h> #include <stdlib.h> int s[10000],n,i; int cmp(const void *a, const void *b){ return(*(int *)a-*(int *)b); //return *(int *)a - *(int *)b; //升序排序 //return *(int *)b - *(int *)a; //降序排序 } int main(){ srand((unsigned)time(NULL)); for(unsigned i=0;i<n;i++) a[i]=rand(); //亂數產生100個數字 qsort(s,n,sizeof(s[0]),cmp); for(i=0;i<n;i++) printf(" %d\t ", s[i]); printf("\n"); return(0); }
No.3.对double型数组排序,原理同int
这里做个注释,本来是因为要判断如果a==b返回0的,但是严格来说,两个double数是不可能相等的,只能说fabs(a-b)<1e-20之类的这样来判断,所以这里只返回了1和-1 #include <stdio.h> #include <stdlib.h> double s[1000]; int i,n; int doublecmp1(const void * a, const void * b){ return((*(double*)a-*(double*)b>0)?1:-1); //return *(double*)a - *(double*)b; //升序排序 //return *(double*)b - *(double*)a; //降序排序 } int doublecmp2(const void *d1,const void *d2){ //return *(double*)d1 - *(double*)d2; //出现错误double不精确的 double tmp=*(double*)d1 - *(double*)d2; if(fabs(tmp) < 0.000001) return 0; else return tmp>0 ? 1 : -1; } int main(){ scanf("%d",&n); for(i=0;i<n;i++) scanf("%lf",&s[i]); qsort(s,n,sizeof(s[0]),doublecmp2); for(i=0;i<n;i++) printf("%lf ",s[i]); return(0); }
No.4.对一个字符数组排序.原理同int #include <stdio.h> #include <string.h> #include <stdlib.h> char s[10000],i,n; int cmp(const void *a,const void *b){ return(*(char *)a-*(char *)b); }
int main(){ scanf("%s",s); n=strlen(s); qsort(s,n,sizeof(s[0]),cmp); printf("%s",s); return(0); }
No.5.对结构体排序 注释一下.很多时候我们都会对结构体排序,比如校赛预选赛的那个樱花,一般这个时候都在cmp函数里面先强制转换了类型,不要在return里面转,我也说不清为什么,但是这样程序会更清晰,并且绝对是没错的. 这里同样请注意double返回0的问题 #include <stdio.h> #include <stdlib.h> struct node{ double date1; int no; } s[100]; int i,n; int cmp(const void *a,const void *b){ struct node *aa=(node *)a; struct node *bb=(node *)b; return(((aa->date1)>(bb->date1))?1:-1); } int main(){ scanf("%d",&n); for(i=0;i<n;i++){ s[i].no=i+1; scanf("%lf",&s[i].date1); } qsort(s,n,sizeof(s[0]),cmp); for(i=0;i<n;i++) printf("%d %lf\n",s[i].no,s[i].date1); return(0); }
No.6.对结构体排序.加入no来使其稳定(即data值相等的情况下按原来的顺序排) #include <stdio.h> #include <stdlib.h> struct node{ double date1; int no; } s[100]; int i,n; int cmp(const void *a,const void *b){ struct node *aa=(node *)a; struct node *bb=(node *)b; if(aa->date1!=bb->date1) return(((aa->date1)>(bb->date1))?1:-1); else return((aa->no)-(bb->no)); } int main(){ scanf("%d",&n); for(i=0;i<n;i++){ s[i].no=i+1; scanf("%lf",&s[i].date1); } qsort(s,n,sizeof(s[0]),cmp); for(i=0;i<n;i++) printf("%d %lf\n",s[i].no,s[i].date1); return(0); }
No.7.对字符串数组的排序(char s[][]型) #include <stdio.h> #include <string.h> #include <stdlib.h> char s[100][100]; int i,n; int cmp(const void *a,const void *b){ return(strcmp((char*)a,(char*)b)); } int main(){ scanf("%d",&n); for(i=0;i<n;i++) scanf("%s",s[i]); qsort(s,n,sizeof(s[0]),cmp); for(i=0;i<n;i++) printf("%s\n",s[i]); return(0); }
No.8.对字符串数组排序(char *s[]型) #include <stdio.h> #include <string.h> #include <stdlib.h> char *s[100]; int i,n; int cmp(const void *a,const void *b){ return(strcmp(*(char**)a,*(char**)b)); /*这里为什么是 *(char**)呢?比较函数compare的参数都是数组元素的地址。如果是 int[],那么其元素就是int[i],传入compare函数的参数为&int[i],那么要比较的话,void *a转换为 int*之后再取出int[i]的值。同样,对于字符串数组s[n][m]或者char*s[]而言,s[i]存放的就是各个串的首地址,s[i]作为元素传递给compare时需要传递的是s[i]的地址,也就是&s[i]。因为s[i]是char*类型的,所以&s[i]是char * *类型的,这样当从void * a指针转换时需要先转换为(char * *)a,而后为了获取对应的字符串再*((char * *)a)即可。*/ } int main() { scanf("%d",&n); for(i=0;i<n;i++) { s[i]=(char*)malloc(sizeof(char*)); scanf("%s",s[i]); } qsort(s,n,sizeof(s[0]),cmp); for(i=0;i<n;i++) printf("%s\n",s[i]); return(0); }
No.1.手工实现QuickSort
实现一: #include <stdio.h> int a[100],n,temp; void QuickSort(int h,int t) { if(h>=t) return; int mid=(h+t)/2,i=h,j=t,x; x=a[mid]; while(1) { while(a[i]<x) i++; while(a[j]>x) j--; if(i>=j) break; temp=a[i]; a[i]=a[j]; a[j]=temp; } a[mid]=a[j]; a[j]=x; QuickSort(h,j-1); QuickSort(j+1,t); return; } int main() { int i; scanf("%d",&n); for(i=0;i<n;i++) scanf("%d",&a[i]); QuickSort(0,n-1); for(i=0;i<n;i++) printf("%d ",a[i]); return(0); } 实现二: int Partition(int *array, int left, int right){ int priot = array[left]; int first = left; int last = right; while(first < last){ while(first < last && array[last] >= priot){ last--; } Swap(&array[first], &array[last]); while(first < last && array[first] <= priot){ first++; } Swap(&array[first], &array[last]); } return first; } void QuickSort(int *array, int first, int last){ if(array == NULL){ return; } if(first < last){ int priot = Partition(array, first, last); QuickSort(array, first, priot - 1); QuickSort(array, priot + 1, last); } } int main(){ int i; scanf("%d",&n); for(i=0;i<n;i++) scanf("%d",&a[i]); QuickSort(a,0,n-1); for(i=0;i<n;i++) printf("%d ",a[i]); return(0); }
关于快排的一些小知识:
1. 快排是不稳定的,这个不稳定一个表现在其使用的时间是不确定的,最好情况(O(n))和最坏情况(O(n^2))差距太大,我们一般说的O(nlog(n))都是指的是其平均时间.
2. 快排是不稳定的,这个不稳定表现在如果相同的比较元素,可能最终顺序会变化,假设我们有这样一个序列,3,3,3,但是这三个3是有区别的,我们标记为3a,3b,3c,快排后的结果不一定就是3a,3b,3c这样的排列,所以在某些特定场合我们要用结构体来使其稳定(No.6的例子就是说明这个问题的)
3. 快排的比较函数的两个参数必须都是const void *的,这个要特别注意,写a和b只是我的个人喜好,写成cmp也只是我的个人喜好.推荐在cmp里面重新定义两个指针来强制类型转换,特别是在对结构体进行排序的时候
4. 快排qsort的第三个参数,那个sizeof,推荐是使用sizeof(s[0])这样,特别是对结构体,往往自己定义2*sizeof(int)这样的会出问题,用sizeof(s[0)既方便又保险
5. 如果要对数组进行部分排序,比如对一个s[n]的数组排列其从s[i]开始的m个元素,只需要在第一个和第二个参数上进行一些修改:qsort(&s[i],m,sizeof(s[i]),cmp);