冒泡排序函数:
冒泡排序函数是c语言中比较经典的排序函数,他对数组的排序可以很好实现,正序,逆序都没问题。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int arr[] = { 9,6,5,4,8,7,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz - 1; i++)//10个元素,
//只需要进行9趟排序,n个元素,只需要进行n-1趟排序
{
for (int j = 0; j < sz - 1 - i; j++)
//第一次排序时,10个元素只需要比9次,第二次排序时
//对第一次排序时已经移动到最前面的数据,不需要再进行比较
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
关键点
for (i = 0; i < sz - 1; i++)//10个元素,
//只需要进行9趟排序,n个元素,只需要进行n-1趟排序
{
for (int j = 0; j < sz - 1 - i; j++)
//第一次排序时,10个元素只需要比9次,第二次排序时
//对第一次排序时已经移动到最前面的数据,不需要再进行比较//所以这里每一次比较的次数 j 范围是 sz - 1 - i
但是冒泡排序不能实现任意类型的数据排序 ,所以这里就体现出了qsort函数的便捷性。
( 但是在使用qsort函数时需要自己写出一个某种类型的比较函数 )
我们先来了解一个qsort函数的定义方式:
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
void qsort(void* base, // 空类型指针,
// 可以接收任意类型地址 用来接收待排序数据元素首地址
size_t num, //接收待排序数据的个数
size_t width, //待排序数据的每个元素位宽,每个元素占字节个数。
int(* compare)(const void* elem1, const void* elem2))
//自己定义所需要的某种类型比较函数
还是通过一个整形数组的例子来了解一个
这里需要自己定义一个 int cmp_int(void const * p1, void const * p2) 函数。
我们再来看一种对结构体类型数据排序的例子
首先定义结构体:
struct stu
{
int age;
char name[20];
};
然后自定义出两种排序结构体的函数:
int sort_by_age(const void * p1,const void * p2)//通过年纪排序
{
return (((struct stu*)p1)->age) - (((struct stu*)p2)->age);//年纪整型数据,直接减
}
int sort_by_name(void const* p1, void const* p2)//通过名字排序
{
return strcmp( ( ( struct stu * ) p1 )->name, ( ( struct stu * )p2 )->name);//字符的比较要通过strcmp函数实现。
}
实现结果如下:
代码如下:
struct stu
{
int age;
char name[20];
};
int sort_by_age(const void * p1,const void * p2)
{
return (((struct stu*)p1)->age) - (((struct stu*)p2)->age);
}
int sort_by_name(void const* p1, void const* p2)
{
return strcmp( ( ( struct stu * ) p1 )->name, ( ( struct stu * )p2 )->name);
}
int main()
{
struct stu s[] = { {23,"zhangsan"} ,{34,"lisi"} ,{15,"wanwu"}};
size_t sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), sort_by_name);
printf("通过名字排序:\n");
for (int i = 0; i < sz; i++)
{
printf("%d %s\n", s[i].age, s[i].name);
}
printf("\n\n");
printf("通过年纪排序:\n");
qsort(s, sz, sizeof(s[0]), sort_by_age);
for (int i = 0; i < sz; i++)
{
printf("%d %s\n", s[i].age, s[i].name);
}
return 0;
}
自定义的qsort函数
这个函数最关键的一个点在于两个数据的交换。
因为在官方定义的qsort函数里,我们只需要提供一个比较函数,返回比较的值,返回的值大于0(前面的值大于后面的值) ,交换两个数据,小于0(前面的值小于后面的值)不交换。
那对于这些不知道类型的数据要怎么进行交换呢 ???
我们都知道,数据在内存中都是以二进制的形式存储的,存储的都是二进制码,而最小的数据类型是一个字节的大小,所有的其他数据类型都是一个字节的整数倍。那么我们可不可以对两个数据进行一个字节一个字节的交换呢。
下面我们对两个int类型的数据进行一个字节一个字节交换的测试:
可以看到结果是如我们预测的那样。对于两个int类型的数据,确实可以进行一个字节一个字节的交换方式。 那么我们只需要知道交换的两种变量类型的字节大小。将其作为循环判断的条件,就可以交换完成两个任意类型的数据了。
正好在qsort函数的定义里 所需要的函数参数有一个就是这个类型数据的位宽。
分析完成 函数代码实现如下:
struct stu
{
int age;
char name[20];
};
int sort_by_age(const void * p1,const void * p2)
{
return (((struct stu*)p1)->age) - (((struct stu*)p2)->age);
}
int sort_by_name(void const* p1, void const* p2)
{
return strcmp( ( ( struct stu * ) p1 )->name, ( ( struct stu * )p2 )->name );
}
void swp(const void * p1, const void * p2, size_t width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *((char*)p1+i);
*((char*)p1+i) = *((char*)p2+i);
*((char*)p2+i) = tmp;
}
}
void my_qsort(void* base,
size_t num,
size_t width,
int(* com)(const void* elem1, const void* elem2) )
{
int i = 0;
for (i = 0; i < num - 1 ; i++)
{
for (int j = 0; j < num - 1 - i; j++)
{
if ( com( ( (char*)base + j * width ) ,( (char*)base + (j + 1)*width )) > 0 )
{
swp( (char*)base + j * width , (char*)base + (j + 1) * width , width );
}
}
}
}
int main()
{
struct stu s[] = { {23,"zhangsan"} ,{34,"lisi"} ,{15,"wanwu"}};
size_t sz = sizeof(s) / sizeof(s[0]);
my_qsort(s, sz, sizeof(s[0]), sort_by_name);
printf("通过名字排序:\n");
for (int i = 0; i < sz; i++)
{
printf("%d %s\n", s[i].age, s[i].name);
}
printf("\n\n");
printf("通过年纪排序:\n");
my_qsort(s, sz, sizeof(s[0]), sort_by_age);
for (int i = 0; i < sz; i++)
{
printf("%d %s\n", s[i].age, s[i].name);
}
return 0;
}