0


初步认识qsort函数以及利用冒泡排序进行初步模拟

首先初步认识一下

qsort 是一个库函数,包含在头文件stdlib.h中

作用:基于快速排序算法实现的一个排序函数

接下来认识这个函数所需要的参数有四个。接下来分别进行分析。

首先我们发现数据起始位置的指针用的是void*来接受,为啥呢???

因为我们不知道我们将来比较的数据类型为什么,有可能是int* 或者char* 又或者是结构体指针,所以我们在这放置了一个void来接受,void可理解为通用类型指针,可能就有人问了,既然有这个万金油的指针类型,那我们还要其他指针类型干什么??下面给出了原因。

我们发现我们自己需要定义一个比较函数,参数类型为两个指针变量,返回类型为int 。

但是为什么这里的指针类型需要定义为void* 类型呢???

这里就需要我们明白指针类型的意义。

指针类型其实决定了,对指针解引用的时候有多大的权限(能操作几个字节)。

void* 可以接受任意类型的指针,但是确定也比较明显,就是在对指针解引用操作的时候,计算机不知道它有多大的权限(可以向后访问几个字节)。

由于我们并不知道我们所要比较的元素是哪种类型的,就采用空类型来接受。而在比较的时候,我们需要将其强制转化成比较的类型,方可进行比较。具体请看下面代码展示。

对这个函数有一个初步了解之后,我们开始尝试使用这个函数。

int cmp_int(const void* e1, const void* e2)
{
    if (*(int*)e1 > *(int*)e2)
        return 1;
    else if (*(int*)e1 < *(int*)e2)
        return -1;
    else
        return 0;
    //return *(int*)e1 - *(int*)e2;
    //这种方式其实更好,利用前者减去后者,差返回就好。
    //这样刚好满足这个函数的定义
    //e1>e2 返回 大于0 <==>  e1-e2>0
    //e1<e2 返回 小于0 <==>  e1-e2<0
    //e1=e2 返回    0 <==>  e1-e2=0

}
void my_print(int arr[], int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", *(arr+i));
    }
}

int main()
{
    int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), cmp_int);
    my_print(arr, sz);

    return 0;
}

在这个过程中,我们需要比较的是int类型的元素,故将额e1和e2强制类型转换成int* 。

然后我们用qsort函数排序一下结构体。

int cmp_stu_by_age(const void* e1, const void* e2)
{
     return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
void test3()
{
    struct Stu arr[3] = { {"zhangsan", 20, 55.5},{"lisi", 30, 88.0},{"wangwu", 10, 90.0} };
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}
int main()
{
    test3();
    
    return 0;
}

经过排序后的结果如下图:

我们发现年龄有原来的无序变为升序。

可以得出一个结论:利用qsort函数可以将一个无序的元素变为升序。那这里有人好奇了,那怎样才能变为降序呢???

其实我们只需要改变逻辑即可!原来我们创建了一个比较函数,其逻辑为

我们颠倒逻辑改为

这样原来是升序,逻辑转换一下,就变为将序了。

经过前面的解析,已经初步明白了qsort函数的使用,我们发现qsort可以排列各种类型的数据,

那我们可以使用冒泡排序进行一个模拟,使我们的冒泡排序也可以万能呢?

首先观察一下我们原有的冒泡排序

void bubble_sort(int arr[], int sz)
{
    int i = 0;
    for (i = 0; i < sz - 1; i++)
    {
        int j = 0;
        for (j = 0; j < sz-1-i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}
void print_arr(int arr[], int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
}
int main()
{
    int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz =  sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, sz);
    print_arr(arr,sz);
    return 0;
}

发现要使冒泡排序可以排序任何元素,要进行一些改动。

经过初步的分析,我们发现我们的冒泡排序还缺少某些参数。例如每个元素的宽度,如若知道这个整体的起始地址,再知道每个元素的宽度和元素的个数。

我们注意到,一旦冒泡排序的元素个数确定了,冒泡排序的趟数也就确定了。

其次,我们发现不同元素之间的比较方式不同,所以我们应该将比较方式提取出来,不同的数据类型采用不同的比较方式。所以就个人自行定义比较方式。

所以我们 的冒泡排序的整体大致为

void bubble_sort(void* base, int num, int width, int (*cmp)(const void*e1, const void*e2))

//base为数据的起始地址
//num为元素的个数
//width为每个元素的大小(字节)
//cpm为比较函数

由于我们无法提前得知比较类型的数据,故我们的比较函数的指针类型也应该为void*,以便存放各种类型的指针。

开始模拟

先写出整体的大致思路,然后进行修改。


//冒泡排序
void my_qsort(void* base, int num, int width, int(*com_int)(const void* e1, const void* e2))
{
    
    
        int i = 0;
        int j = 0;
        for (i = 0; i < num - 1; i++)
        {
            for (j = 0; j < num - 1 - i; j++)
            {
                /*
                 *if(>0)
                 *swap
                 *
                 *这里就是需要我们进行改变的。
                 *
                 */
            }
        }
    
}

int main()
{
    int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    my_qsort(arr, sz, sizeof(arr[0]), com_int);
    //打印数组
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

我们思考如何应该如何让判断当前两个元素的大小???

先写出我们的一个比较函数

首先观察我们原来的比较函数,如若前者大于后者,则进行交换。

类比与这个,我们可以写出


//比较函数
int com_int(const void* e1, const void* e2)
{
    return *(int*)e1 - *(int*)e2;
}

如果cmp-int(,)大于0,则前面的数据大于后面的数据,则进行交换。

如果cmp-int(,)小于或者等于0,则前面的数据小于后面的数据,则不进行交换。

if(cmp(,)>0)
{
    //交换;
}

我们发现这个比较函数需要那两个待比较元素的首地址,可是我们该如何拿到这两个元素的首地址呢????

我们需要计算出待比较元素的地址

如何计算呢??

我们以整形数组为例进行演示:

那么如何寻找相邻的待比较元素的地址呢??

再看一下这张图,我们来分析一下。其实这个过程中,相邻元素的下表一个是就 j ,一个是 j+1 。

//arr[j] <==>(char*)base + width*j
//arr[j+1] <==>(char*)base + width*(j+1)

所以判断函数可以写成

if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
            {
                
                /*
                 *
                 * 此处是交换函数;
                 * 
                 */
            }

现在我们来编写交换函数swap

首先交换函数需要传输 待交换元素的地址 , 如果传输 待交换元素的值 ,无法达到交换的效果。但是需要注意的是我们传输过去的是一个元素的首地址(char*),但是我们不知道一个元素有几个字节(就是一个元素的宽度)?我们知道,交换元素意味着元素的整体都进行交换,而不能只交换部分。故我们也应该将宽度(大小)传过去。

if (com_int((char*)a + j * width, (char*)a + (j + 1) * width)>0)
                {
                    swap((char*)a + j * width, (char*)a + (j + 1) * width, width);

                }

以整形举例,方便大家理解这个交换过程。

由于 swap的参数类型为cahr* 类型,一次只能交换一个字节,故需要交换width(即元素的宽度或者大小)次 。故函数可以写为:

void swap(char* p1,char*p2,int width)
{
    for (int n = 0; n < width; n++)
    {
        char tmp = *(p1 + n);
        *(p1 + n) = *(p2 + n);
        *(p2 + n) = tmp;

    }
}

通过单个字节的交换,实现元素的交换。

最终实现了利用冒泡排序实现qsort万能排序的功能。整体代码在下

//比较函数
int com_int(const void* e1, const void* e2)
{
    return *(int*)e1 - *(int*)e2;
}

void swap(char* p1,char*p2,int width)
{
    for (int n = 0; n < width; n++)
    {
        char tmp = *(p1 + n);
        *(p1 + n) = *(p2 + n);
        *(p2 + n) = tmp;

    }
}
void my_qsort(void* base, int num, int width, int(*com_int)(const void* e1, const void* e2))
{
    
    
        int i = 0;
        int j = 0;
        for (i = 0; i < num - 1; i++)
        {
            for (j = 0; j < num - 1 - i; j++)
            {
                
                if (com_int((char*)a + j * width, (char*)a + (j + 1) * width)>0)
                {
                    swap((char*)a + j * width, (char*)a + (j + 1) * width, width);

                }
            }
        }
    
}
int main()
{
    int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    my_qsort(arr, sz, sizeof(arr[0]), com_int);
    //打印数组
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}
标签: c语言

本文转载自: https://blog.csdn.net/m0_63711643/article/details/123595642
版权归原作者 Vegetable——dog 所有, 如有侵权,请联系我们删除。

“初步认识qsort函数以及利用冒泡排序进行初步模拟”的评论:

还没有评论