排序是指将元素集合按照规定的顺序排列,通常有2中方法,升序和降序排序。排序的目的就是是数据能够以更有意义的形式表现出来。虽然排序最显著的应用时排列数据以显示它,但它往往可以用来解决其他的问题,特别是作为成熟算法的一部分。
总的来说,排序的算法有2大类:比较排序和线性时间排序。比较排序依赖于比较和交换来讲元素移动到正确的位置上。令人惊讶的是,并不是所有的排序算法都依赖于比较。对于那些依赖于比较来进行排序的算法来说,它的时间复杂度一般是O(nlgn)。对于线性排序,从它的名字可以看出,它的时间复杂度是O(n)。线性排序依赖于数据集合的某种特征,并不是所有的场合都适合,下面就简单介绍几种排序的方法:
1、 交换(冒泡)和选择:直接看代码吧,比较简单
int main()
{
int min,i,j;
int s[N]={14,27,9,55,16,48,31,22};
quicksort(s,0,N-1);
#if 0
for(i=N-1;i>0;i--)//,排序的次数 交换排序,冒泡排序
{
min=0;
for(j=0;j<i;j++)//查找剩余数组中最大的元素排低
if(s[j]>s[j+1])
{
min =1;
s[j]^=s[j+1];
s[j+1]^=s[j];
s[j]^=s[j+1];
}
if(min==0)//当min仍为0时,说明已经排序完成,直接跳出循环;
break;
}
for(i=0;i<N-1;i++)//排序所需要的趟数,选择排序
{
min = i;
for(j=i+1;j<N;j++)
if(s[min] > s[j])
min = j;
if(min>i)
{
s[min]^=s[i];
s[i]^=s[min];
s[min]^=s[i];
}
}
#endif
for(i=0;i<N;i++)
printf("%d ",s[i]);
puts("");
return 0;
}
2、插入排序
插入排序时一种较为简单的算法但是它在处理大型数据集合时并不高效,因为在决定把元素插入到那个位置前需要将插入元素与元素集合进行比较。
/* inssort.c*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/******************************************************
*插入排序(通用的函数接口)
*参数:data:数组data中的元素
* size:数组元素的个数
* esize:每个元素的大小
* compare:函数指针,比较函数大小的函数
*返回值:成功返回0,否则-1;
* ***************************************************/
int inssort(void *data, int size, int esize,int (*compare)(const void* key1,const void *key2))
{
char *a = data;
void *key = NULL;
int i,j;
/*位key申请空间*/
if ((key = (char *)malloc(esize)) == NULL)
return -1;
for(j = 1; j < size; j++){
memcpy(key, &a[j*esize],esize);
i = j - 1;
while (i >= 0 && compare(&a[i * esize],key) > 0){
memcpy(&a[(i + 1)*esize], &a[i * esize], esize);
i--;
}
memcpy(&a[(i + 1 )*esize], key, esize);
}
free(key);
return 0;
}
/*比较函数的实现,需要注意,我在测试中一直不正确,确定就是在这书写不正确导致的*/
int compare(const void *key1,const void *key2)
{
// printf ("key1 =%d,key2 = %d \n",*((int *) key1),*((int *)key2));
if (*((int*)key1) > *((int *)key2))
return 1;
return 0;
}
int main()
{
int a[5] = {20,30,10,5,25};
int i;
if(inssort(a,5,sizeof(int),compare) < 0)
printf("error !!!!");
for (i = 0; i < 5; i++)
printf("%d\n",a[i]);
return 0;
}
3 快速排序
快速排序是一种分治排序算法,被认为是解决一般问题的最佳排序方法。由于快速排序是一种分治算法,因为可以用分治算法的思想将排序分为三个步骤,这样将有助于我们理解:
1 、 分: 设定一个分隔值并将数据分为两部分。
2、 治:分别在两部分用递归的方式继续使用快速排序法。
3、 合:对分割部分排序直至完成。
选择有效的分隔值是提高快速排序的有效方法。
假设要排序的数组是A[1]……A[N],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一躺快速排序。一躺快速排序的算法是:
1)、设置两个变量I、J,排序开始的时候I:=1,J:=N;
2)以第一个数组元素作为关键数据,赋值给X,即X:=A[1];
3)、从J开始向前搜索,即由后开始向前搜索(J:=J-1),找到第一个小于X的值,两者交换;
4)、从I开始向后搜索,即由前开始向后搜索(I:=I+1),找到第一个大于X的值,两者交换;
5)、重复第3、4步,直到I=J;
示例
待排序的数组A的值分别是:(初始关键数据:key=49) 注意关键key永远不变,永远是和key进行比较,无论在什么位置,最后的目的就是把key放在中间,小的放前面大的放后面。
A[0] | A[1] | A[2] | A[3] | A[4] | A[5] | A[6] |
49 | 38 | 65 | 97 | 76 | 13 | 27 |
进行第一次交换后:27 38 65 97 76 13 49
( 按照算法的第三步从后面开始找,此时:J=6)
进行第二次交换后:27 38 49 97 76 13 65
( 按照算法的第四步从前面开始找>key的值,65>49,两者交换,此时:I=2 )
进行第三次交换后:27 38 13 97 76 49 65
( 按照算法的第五步将又一次执行算法的第三步从后开始找
进行第四次交换后:27 38 13 49 76 97 65
( 按照算法的第四步从前面开始找大于key的值,97>49,两者交换,此时:I=3,J=5 )
此时再执行第三步的时候就发现I=J=3,从而结束一趟快速排序,那么经过一趟快速排序之后的结果是:27 38 13 49 76 97 65,即所有大于key49的数全部在49的后面,所有小于key(49)的数全部在key(49)的前面。
void quicksort(int s[],int low, int high)//快速排序
{
int i = low,j=high;
int key=s[i];
while(i<j)
{
while(i<j&&s[j] > key)
j--;
if(i<j)
s[i] = s[j];
while(i<j&&s[i]< key )
i++;
if(i<j)
s[j] = s[i];
}
s[i]=key;
if(i-1>low)quicksort(s,low,i-1);
if(i+1<high)quicksort(s,i+1,high);
}
下面是典型的分治算法实现快速排序
/*qksort.c*/
#include <stdio.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int inssort(void *data, int size, int esize,int (*compare)(const void* key1,const void *key2))
{
char *a = data;
void *key = NULL;
int i,j;
/*位key申请空间*/
if ((key = (char *)malloc(esize)) == NULL)
return -1;
for(j = 1; j < size; j++){
memcpy(key, &a[j*esize],esize);
i = j - 1;
while (i >= 0 && compare(&a[i * esize],key) > 0){
memcpy(&a[(i + 1)*esize], &a[i * esize], esize);
i--;
}
memcpy(&a[(i + 1 )*esize], key, esize);
}
free(key);
return 0;
}
/*compare_int */
static int compare_int(const void *int1, const void * int2)
{
printf("int1 = %d. int2 = %d \n",*(const int *)int1,*(const int *)int2);
if (*(const int *)int1 > *(const int *)int2)
return 1;
else if (*(const int *)int1 < *(const int *)int2)
return -1;
else
return 0;
}
/*分区*/
static int partition(void *data, int esize,int i,int k, int (*compare)(const void *key1,const void *key2))
{
char *a = data;
void *pval = NULL,
*temp = NULL;
int r[3] = {'\0'};
if ((pval = (char *)malloc(esize)) == NULL)
return -1;
if ((temp = (char *)malloc(esize)) == NULL){
free(pval);
return -1;
}
/*获得分割值*/
r[0] = (rand() % (k - i + 1)) + i;
r[1] = (rand() % (k - i + 1)) + i;
r[2] = (rand() % (k - i + 1)) + i;
// inssort(r,3,sizeof(int),compare);
memcpy(pval,&a[r[1]*esize],esize);
i--;
k++;
while (1){
do{
k--;
}while (compare(&a[k*esize],pval) > 0);
do{
i++;
}while (compare(&a[i*esize],pval)<0);
if (i >= k){
break;
} else {
memcpy(temp,&a[i*esize],esize);
memcpy(&a[i*esize],&a[k*esize],esize);
memcpy(&a[k*esize],temp,esize);
}
}
free(pval);
free(temp);
return k;
}
int qksort(void *data,int size,int esize,int i,int k,int (*compare)(const void * key1,const void * key2))
{
int j;
while(i < k){
/*找到在那进行分区*/
if ((j = partition(data,esize,i,k,compare)) < 0)
return -1;
if (qksort(data,size,esize,i,j,compare) < 0)
return -1;
i = j + 1;
}
return 0;
}
int main()
{
int a[7]={10,20,5,6,18,25,30};
qksort(a,7,sizeof(int),0,6,compare_int);
int i;
for (i =0 ; i< 7; i++)
printf("%d\n",a[i]);
}
快速排序的一个用法:
/*********************************************************************
* directl.c
*快速排序在目录列表中的用法,和命令LS是一样的
* **************************************************************/
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXNAMELEN 16
/*定义一个结构体保存目录名*/
typedef struct Directory_ {
char name[MAXNAMELEN + 1];
}Directory;
/*函数接口*/
int directory(const *path,Directory *dir);
/*比较目录名字大小*/
static int compare_dir(const void * key1, const void * key2)
{
int retval;
if ((retval = strcmp (((const Directory *)key1) -> name, ((const Directory *)key2) ->name)) > 0)
return 1;
else if (retval < 0)
return -1;
else
return 0;
}
/* ****************************************************************
*利用排序,把当前路径下的目录名或者文件名保存到一个数组中吧,并进行排序
*参数: path : 当前路径
* dir : 二级指针,但是在代码中并未用到二级指针,一直用*dir;
*返回值,返回当前路径下文件数
* */
int directl(const char *path, Directory **dir)
{
DIR *dirptr;
Directory * temp;
struct dirent * curdir;
int count = 0,
i;
if ((dirptr = opendir(path)) == NULL)
return -1;
*dir = NULL;
while ((curdir = readdir (dirptr)) != NULL){
count ++;
if ((temp = (Directory *) realloc(*dir,count *sizeof(Directory))) == NULL){
free(*dir);
return -1;
} else {
*dir = temp;
}
strcpy (((*dir)[count - 1]).name,curdir->d_name);
}
closedir(dirptr);
if (qksort(*dir,count,sizeof(Directory),0,count - 1,compare_dir) != 0)
return -1;
return count;
}