qsort各种用法大全以及实现

Posted 梦乡回雪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了qsort各种用法大全以及实现相关的知识,希望对你有一定的参考价值。

详解qsort各种用法以及实现



前言

  • 在文章开头,我们先来说一下qsort的作用。在正常我们写完一个冒泡排序的时候比如void Bubble_sort(int arr[],int sz);那么我们在用这个函数的时候就只能去给一个整型数组大小排序,当我们想要去排序浮点型,字符型的时候该函数就不适用了,而qsort函数就能解决这个问题,只要你规定了排序方法,你就可以去排序字符,整型,浮点,结构体,做到万物皆可排。

一、qsort定义

首先我们打开MSDN,搜索一下qsort

  • 作用:Performs a quick sort.(执行快排)
  • 头文件:<stdlib.h>
  • 参数和返回类型:
void qsort( 
		void *base, //接收所要排序的目标数组名
		size_t num, //接收排序数组的个数(以元素为单位)
		size_t width, //每一个元素大小(以字节为单位)
		int (__cdecl *compare )(const void *elem1, const void *elem2 ) //自定义的比较方法
		);
  • 这里要注意qsort函数里的void* , 这里一旦写成其他类型都会只能对一种数据类型排序,而空类型void*正好可以接收其他任何类型,从而当做参数!

  • 自定义比较函数
    这里MSDN上给出了一些关于该函数的说明,具体大家可以自行查看

  • 参数类型:
    int compare( const void * elem1,const void * elem2 );

  • 返回值:

    并且注意按上边这种返回值去写比较函数的话,最终是得到的是升序排列。
    那么返回值相反就会得到降序排列。

二、qsort排序应用

1、整型排序

  • 首先我们给出一个乱序整型数组,
			int arr[] = { 1,3,4,8,2,6,7,5,9,10 };
  • 接着按要求去写qsort函数
		int sz = sizeof(arr) / sizeof(arr[0]);
		qsort(arr, sz, sizeof(arr[0]), cmp_int);
  • 实现自定义cmp_int函数

那么这里也就是第一个参数小于第二个参数的话返回值 < 0;
第一个参数大于第二个参数的话返回值 > 0;
第一个参数等于第二个参数的话返回值 = 0;

  • 具体实现:
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

对传进来的e1和e2用void* 来接收,并且考虑到使用的时候是对整型数组排序,因此强转成int * ,
用e1-e2,e1 > e2,返回值大于0,e1 < e2,返回值小于0,e1 = e2,返回值等于0,满足要求。

完整代码:

#include<stdio.h>
#include<stdlib.h>
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
int main()
{
	int arr[] = { 1,3,4,8,2,6,7,5,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	
	return 0;
}

运行结果:

  • 那么如果你想要实现降序只需修改一下自定义比较函数的返回值(e2-e1)即可。
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e2 - *(int*)e1;
}

2、字符型排序

  • 我们知道字符是以ASC码中对应的数字表示的,因此在写比较函数时直接相减即可。
#include<stdio.h>
#include<stdlib.h>
int cmp_char(const void* e1, const void* e2)
{
	return *(char*)e1 - *(char*)e2;
}
int main()
{
	char a[] = { 'a','c','e','d','b' };
	int sz = sizeof(a) / sizeof(a[0]);
	qsort(a, sz, sizeof(a[0]), cmp_char);
	for (int i = 0; i < sz; i++)
	{
		printf("%c ", a[i]);
	}
	
	return 0;
}

3、浮点型排序

  • 这里只需要注意浮点型的数据储存在计算机中是有误差的,可能在你的电脑是1,那么其他电脑上就是2了。因此浮点数相等的比较不能直接相减,只能用fabs(a-b)<1e-20之类的这样来判断,所以这里只返回了1和-1。
#include<stdio.h>
#include<stdlib.h>
int cmp_double(const void* e1, const void* e2)
{
	return ((*(double*)e1 - *(double*)e2) > 0)? 1:-1;
}
int main()
{
	double a[] = { 1.2,56.4,0.56,456.89,32.4 };
	int sz = sizeof(a) / sizeof(a[0]);
	qsort(a, sz, sizeof(a[0]), cmp_double);
	for (int i = 0; i < sz; i++)
	{
		printf("%.2f ", a[i]);
	}
	
	return 0;
}

4、字符串排序

#include<stdio.h>
#include<stdlib.h>
int cmp_string(const void* e1, const void* e2)
{
	return strcmp((char*)e1, (char*)e2);
}
int main()
{
	char a[5][6] = { "h","he","hel","hell","hello" };
	int sz = sizeof(a) / sizeof(a[0]);
	qsort(a, sz, sizeof(a[0]), cmp_string);
	for (int i = 0; i < sz; i++)
	{
		printf("%s ", a[i]);
	}
	
	return 0;
}
  • 值得注意的就是strcmp函数里边强转类型为char*后正好是 每一串字符的首地址,不用在解引用了。

5、结构体排序

struct student
{
	char name[20];
	int age;
};

这里我们定义了一个结构体,那么接下来按照名字和年龄两种排序情况说明

1. 按照name排序

#include<stdio.h>
#include<stdlib.h>
struct student
{
	char name[20];
	int age;
};
int cmp_name(const void* e1, const void* e2)
{
	return strcmp(((student*)e1)->name, ((student*)e2)->name);
}
void test()
{
	student  s[3] = { {"z",15},{"l",17},{"w",20} };
	qsort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), cmp_name);
	for (int i = 0; i < 3; i++)
	{
		printf("%s ", s[i].name);
	}
}
int main()
{
	test();
	return 0;
}

字典序结果:

2. 按照年龄排序:

#include<stdio.h>
#include<stdlib.h>
struct student
{
	char name[20];
	int age;
};
int cmp_age(const void* e1, const void* e2)
{
	return ((student*)e1)->age - ((student*)e2)->age;
}
void test1()
{
	student  s[3] = { {"张三",15},{"李四",17},{"王伟",20} };
	Bubble_sort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), cmp_age);
	for (int i = 0; i < 3; i++)
	{
		printf("%s ", s[i].name);
	}
}
int main()
{
	test1();
	return 0;
}

三、实现qsort

这里我们采用冒泡排序来实现qsort函数:

void Bubble_sort(void* base, size_t num, size_t width, int(* compare)(const void* elem1, const void* elem2))
{
	
	for (size_t i = 0; i < num; i++)//冒泡排序的趟数
	{
		for (size_t j = 0; j < num - 1 - i;j++)//每一趟
		{
			//判断交换
		}
	}
}
  • 那么框架写完了,接下来就是判断大小。规则在compare函数中用户定义,那么怎么传参呢?
  • 由于比较函数要求的是要每个用来比较元素的地址,那么对于第n个元素我们只需要用base + j * width,即可找到第j个元素的地址。
  • 但是要注意这里我们要(char*)base + j * width来强转一下,我们知道char类型1个字节,int 4个字节,各种数据类型中最小的单位都是1个字节,其他的都是其的n倍,因此(char*)强转可以更好的进行以字节为单位的数据交换!!!
void Bubble_sort(void* base, size_t num, size_t width, int(* compare)(const void* elem1, const void* elem2))
{
	
	for (size_t i = 0; i < num; i++)//冒泡排序的趟数
	{
		for (size_t j = 0; j < num - 1 - i;j++)//每一趟
		{
			if (compare((char*)base+j*width,(char*)base+(j+1)*width) > 0)
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
			}
		}
	}
}
  • 接下来就是swap函数的实现了
    对于一个整型数组:

    其实际存储如图:(大端存储)
  • 每一个int型元素都是有四个字节,需要把第一个元素和第二个元素在内存中的数据完全交换,而这里正好验证上边,以字节为单位能够方便很多,宽度是width(单位是字节),只需要把每一个字节的内容对应交换即可!!

第一种写法:

完整代码:

void Swap(char* buffer1, char* buffer2,int width)
{
	for (int i = 0; i < width; i++)
	{

		char t = *buffer1;
		*buffer1 = *buffer2;
		*buffer2 = t;
		buffer1++;
		buffer2++;
	}
}
void Bubble_sort(void* base, size_t num, size_t width, int(* compare)(const void* elem1, const void* elem2))
{
	
	for (size_t i = 0; i < num; i++)//冒泡排序的趟数
	{
		for (size_t j = 0; j < num - 1 - i;j++)//每一趟
		{
			if (compare((char*)base+j*width,(char*)base+(j+1)*width) > 0)
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
			}
		}
	}
}

第二种写法:

void myqsort(void * base, size_t nitems, size_t size, int(*compar)(const void *, const void *))
{
	int i, j;
	char * st = (char *)base; //void *不方便加减,用char *加减轻松且字节跳转为1,方便控制。
	char tmp[16]; //考虑到long double类型,临时空间做成16字节比较保险
 
	for (i = 0; i < nitems - 1; i++)
	{
		for (j = 0; j < nitems - 1 - i; j++) //冒泡常用循环头
		{
			if (compar(st + j * size, st + (j + 1) * size)) //比较的时候跳转注意乘size
			{
				memcpy(tmp, st + j * size, size); //交换操作用memcpy完成就不会出问题。
				memcpy(st + j * size, st + (j + 1) * size, size);
				memcpy(st + (j + 1) * size, tmp, size);
			}
		}
	}
}

qsort库函数的用法

ToString()的各种用法(大全)

有用 .htaccess 使用方法大全

POJ 1065 Wooden Sticks#贪心+qsort用法

qsort函数用法

qsort用法