相信大家对冒泡排序一定不陌生吧,作为一种经典的排序方法,它的基本思想是:(以升序为例)将相邻的两个数进行比较,若前一个数比后一个数大,则交换它们的顺序,这样一趟下来,最大的数就跑到了最后一位,的二趟还是一样的方法,最大的数不参与交换,这样进行n-1趟后,我们就实现了排序;
void BubbleSort(int *dest, int sz)
{
for (int i = 0; i < sz-1; i++)//几趟
{
for (int j = 0; j < sz - i - 1; j++)//每趟判断几个元素
{
if (dest[j] > dest[j + 1])//这里是不同类型判断的核心
{
int temp = dest[j];//这个中间变量也需要不同的类型
dest[j] = dest[j + 1];
dest[j + 1] = temp;
}
}
}
}
但我们说这样的冒泡排序只能实现整形排序,我现在想让它在遇到字符型,短整型,浮点型,甚至结构体,都能够正常的工作,实现一个通用的冒泡排序,正好库函数里有一个qsort()也是通用的,我们先来看看它的结构是甚麽样的吧:
我们看到,qsort为传入的操作数组定义的Void*型,这中类型的指针可以存放任何类型数据的地址,但却无法直接取出,需要将指针进行强制类型转换后才可以,Size_t num是数组的元素的个数,这与上文的一般冒泡排序的使用方式相同,第三个width则是每个元素的大小,我们无法直接告诉函数目标数组的类型,只能退而求其次,传入这个类型的大小,这决定指针的寻址步长,得到正确的数据,最后一个参数,是一个留给使用者定义的比较函数指针,没有人会比使用者更清楚要比较什么样的数据了,所以函数的使用者需要按照这个函数指针类型封装一个返回值为整形,参数为const void*的两个要比较的元素;
我们的发现冒泡排序的核心在于它如何判断dest[j]和dest[j+1]的大小,我们仿照qsort,将目标数组的首元素指针转化为void*型,假设现在需要判断整形数组,我们可以将这个地址强制转换成char*,然后又根据width来制定指针的步长,这样就可以把任何类型的数据提取出来
(char*)basic+j*width,(char*)basic+(j+1)*width
这就是dest[j]和dest[j + 1]
然后传参给使用者自定义的比较函数,如果返回1则表明dest[j]>dest[j + 1],需要交换,非1的话则不用交换
我们这里又封装交换函数,这里直接按字节交换,只需要将中间变量类型定义成字节就行:
void swap(char*elem1, char*elem2,unsigned int width)//按字节交换!这样可以不用判断到底是什么类型的数据
{
assert(elem1 != NULL && elem2 != NULL);
for (unsigned int i = 0; i < width; i++)
{
char temp = *(elem1 + i);
*(elem1 + i) = *(elem2 + i);
*(elem2 + i) = temp;
}
}
最后是完整的代码,这里为了测试我定义了三个比较函数,但实际情况下,根据调用者的需要只用写一个比较函数就行:
//模拟库函数qsort实现通用冒泡排序
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
struct Student
{
char* name;
double tall;
int age;
};
void BubbleSort(int *dest, int sz)//只针对int型数据的一般冒泡排序
{
for (int i = 0; i < sz-1; i++)//几趟
{
for (int j = 0; j < sz - i - 1; j++)//每趟判断几个元素
{
if (dest[j] > dest[j + 1])
{
int temp = dest[j];
dest[j] = dest[j + 1];
dest[j + 1] = temp;
}
}
}
}
int compare_char(const void*elem1, const void*elem2)//需要使用者自己定义
{
return *(char*)elem1 - *(char*)elem2;
}
int compare_int(const void*elem1,const void*elem2)//需要使用者自己定义
{
return *(int*)elem1 - *(int*)elem2;
}
int compare_struct(const void*elem1, const void*elem2)//需要使用者自己定义
{
return ((struct Student*)elem1)->age - ((struct Student*)elem2)->age;
}
void swap(char*elem1, char*elem2,unsigned int width)//按字节交换!这样可以不用判断到底是什么类型的数据
{
assert(elem1 != NULL && elem2 != NULL);
for (unsigned int i = 0; i < width; i++)
{
char temp = *(elem1 + i);
*(elem1 + i) = *(elem2 + i);
*(elem2 + i) = temp;
}
}
void Common_BubbleSort(void*basic, int sz,unsigned int width, int(*cmp)(const void*elem1, const void*elem2))//大体框架相似于冒泡排序不同之处在于判断交换的条件
{
for (int i = 0; i < sz - 1; i++)//几趟
{
for (int j = 0; j < sz - i - 1; j++)//每趟判断几个元素
{
if ((cmp((char*)basic+j*width,(char*)basic+(j+1)*width))>0)
{
swap((char*)basic + j*width, (char*)basic + (j + 1)*width,width);
}
}
}
}
int main()//分别使用整形,字符,和结构体进行测试
{
struct Student init[3] = { {"zhangsan",1.75,19},{"lisi",1.72,13},{"wangwu",1.65,18} };
int arr_int[] = {2,4,3,6,5,1,8,7,9,6};
char arr_char[] = { '2','4','3','6','5','1','8','7','9','6' };
int sz_int = sizeof(arr_int) / sizeof(arr_int[0]);
int sz_char = sizeof(arr_char) / sizeof(arr_char[0]);
int sz_struct = sizeof(init) / sizeof(init[0]);
unsigned int width_int = sizeof(int);
unsigned int width_char = sizeof(char);
unsigned int width_struct= sizeof(struct Student);
//BubbleSort(arr, sz);
Common_BubbleSort(arr_char, sz_char, width_char, compare_char);
for (int i = 0; i < sz_char; i++)
{
printf("%c", *(arr_char + i));
}
printf("\n");
system("pause");
return 0;
}