【冒泡排序】模仿qsort的功能实现一个通用的冒泡排序

简介: 【冒泡排序】模仿qsort的功能实现一个通用的冒泡排序

前言

上一章讲了qsort如何排序各种类型数据,本章继续学习如何模仿qsort的功能实现一个通用的冒泡排序


这个冒泡排序要具备:

1.使用冒泡排序的思想

2.适应于任意类型数据的排序

void* 是实现一个通用的冒泡排序最核心的部位
因为void* 类型的指针可以接收任意类型的地址

假设排序整型

曾经学的冒泡排序存在着一些局限性

解决方法是:

对于问题三,后面会讲到

首先第一步:写一个main()函数,分装一个test1函数

int main()
{
  test2();
  return 0;
}

test1将会模仿qsort的功能:

四个函数的意思是:

void qsort(

void* base,//指向需要排序的数组的第一个元素

size_t num,//排序的元素个数

size_t size,//一个元素的大小,单位是字节

int(*cmp)(const void*, const void*)

);//函数指针类型-这个函数指针指向的函数,能够比较base指向数组中的两个元素

test1函数 用来描写类型的性状

test1函数里的内容具有随变性,不是固定的,但是有一个固定的框架

这里面就写你要对什么数据类型进行冒泡排序。包含四个点:

①写清楚是什么数组,有什么元素。

②一个元素的大小。

③写好等会要调用的bubble_sor函数,包括它的四个参数。

④最后分装一个打印函数,打印结果的时候要调用它。

//代码如下:
void test1()
{
  int arr[] = { 2,1,4,6,5,3,8,0,9,7 };//整型数组,有10个数字
  int sz = sizeof(arr) / sizeof(arr[0]);//一个元素大小
  bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);//模仿qsort函数的参数
  print(arr, sz);//打印函数
}

在test1创建了bubble_int 函数,下一步就是实现它,分两步走

步骤一:写函数参数

bubble_int函数的参数,如何才能接收任意类型数据呢?

图二解决一详细说了解决方法:模仿qsort函数参数

另外在bubble_int函数里,大部分代码都是固定的,可以说是一个模板,不能随意改动,而每种类型的比较方法都是不同的,所以比较的方法就不能写在bubble_int函数里。

需要另外写一个交换函数。在bubble_int函数里调用以下就可以了。

这就是为什么qsort函数的第四个参数是cmp_int函数了。所以bubble_int的第四个参数是cmp_int函数指针。

bubble_int参数总结概括为 传某个类型数组的起始位置+数组个数+一个元素大小+比较方法的函数指针

所以bubble_int的参数如下:

void bubble_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
}

步骤二:写具体代码实现比较

bubble_int里面的内容简单概括为:循环对比两个数

上图指出:不管是整形数据,浮点型数组,还是结构体数据,它们的趟数是不会变的,一趟内部比较的对数也不会变。

所以这个趟数循环的代码也是固定的。

代码如下:

void bubble_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
  int i = 0;
  //趟数
  for (i = 0; i < num - 1; i++)
  {
    int j = 0;
    //一趟内部比较的对数 
    for (j = 0; j < num - 1 - i; j++)
    {
      if()两个元素比较
    }
  }
}

图二说到,在if语句里的问题是:不能简单的让两个数用>来比较。

解决方法是要调用cmp_int函数指针,

所以我们先完善一下cmp_int函数。

cmp_int的两个参数是const修饰的void*指针,两个元素分别命名为p1,p2

比较方法是:两个数作差,就是p1-p2

因为已经知道是整型数组,所以要把p1,p2强制类型转换成整型,再解引用找到p1和p2的值。

注意:记得写return,因为要把作差结果返回给cmp,cmp_int前面也要用int修饰,因为有返回值

int cmp_int(const void*p1, const void*p2)
{
  return (*(int*)p1 - *(int*)p2);
}

接下来继续完善if()里面的内容,

后面的步骤如下:

1.调用cmp函数

2.cmp函数里传两个元素的地址

因为数组类型不同,导致元素个数不同,每个元素的大小也不同,有的是一个字节,有的是四个字节,所以两个元素的地址是不一样的。

要实现通用的冒泡排序,要让cmp函数拿到不同类型的元素,就要传它们的地址过去。

所以有一个通用的地址计算公式,能找到任意数组类型的两个元素的地址。

第一个元素地址(char*)base+j*size,
第二个元素地址(char *)base+(j+1) * size

j表示数组下标,size表示字节大小,(char * )base表示是第一个元素地址,单位是一个字节

解释:

为什么是char类型的指针,因为一个一个字节找更准确

如下图所示:

当cmp接收地址,并把作差结果返回来时,如果结果小于0,就不用两数交换,如果结果大于0才需要交换位置,所以大于0才能进入if语句。

void bubble_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
  int i = 0;
  for (i = 0; i < num - 1; i++)
  {
    int j = 0;
    for (j = 0; j < num - 1 - i; j++)
    {
      if (cmp( (char*)base + j * size, (char*)base + (j + 1) * size) > 0)
      {
      }
    }
  }
}

3.cmp_int的参数用指针接收两个元素的地址

4.cmp_int对两个元素解引用,作差

5.cmp_int返回结果给cmp

6.cmp判断结果是否>0

7.>0,进入循环

8.循环里调用一个交换函数Swap,传两个元素的地址,和大小

调用Swap传参时要把两个元素的地址传过去,还要传两个元素的大小siez,因为不传size,Swap函数不知道要交换多少个字节。

void bubble_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
  int i = 0;
  for (i = 0; i < num - 1; i++)
  {
    int j = 0;
    for (j = 0; j < num - 1 - i; j++)
    {
      if (cmp( (char*)base + j * size, (char*)base + (j + 1) * size) > 0)
      {
        Swap(cmp((char*)base + j * size, (char*)base + (j + 1) * size),size);
      }
    }
  }
}

9.Swap函数接收到buf1和buf2的地址,以及大小

buf1作为地址要用指针接收,而且是char*指针(第2点讲到原因)

一个一个字节交换的

比如要交换5和4,5的地址是05 00 00 00,4的地址是04 00 00 00,

Swap接收到地址和大小,便开始一个字节一个字节交换。

代码如下

void Swap(char* buf1,char* buf2,int size)
{
  char tmp = 0;
  int i = 0;
  for (i = 0; i < size; i++)
  {
    tmp = *buf1;//交换元素,不是交换地址
    *buf1 = *buf2;
    *buf2 = tmp;
    buf1++;//地址往后找一个字节
    buf2++;
  }
}

10.分装打印函数,把结果打印出来

记得函数参数用指针接收,sz本身是地址就不用指针接收

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

总结

完整的代码如下:

#include<stdio.h>
void Swap(char* buf1,char* buf2,int size)
{
  char tmp = 0;
  int i = 0;
  for (i = 0; i < size; i++)
  {
    tmp = *buf1;
    *buf1 = *buf2;
    *buf2 = tmp;
    buf1++;
    buf2++;
  }
}
void print(int* arr, int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
}
int cmp_int(const void*p1, const void*p2)
{
  return (*(int*)p1 - *(int*)p2);
}
void bubble_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
  int i = 0;
  for (i = 0; i < num - 1; i++)
  {
    int j = 0;
    for (j = 0; j < num - 1 - i; j++)
    {
      if (cmp( (char*)base + j * size, (char*)base + (j + 1) * size) > 0)
      {
        Swap((char*)base + j * size, (char*)base + (j + 1) * size,size);
      }
    }
  }
}
void test1()
{
  int arr[] = { 3,5,2,0,7,9,4,1,6,8 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
  print(arr,sz);
}
int main()
{
  test1();
  return 0;
}

以上就是模仿qsort的功能实现一个通用的冒泡排序的方法,希望对您有帮助,感谢关注感谢点赞感谢收藏!

相关文章
|
6月前
|
机器学习/深度学习 搜索推荐 算法
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
|
6月前
|
搜索推荐 算法 大数据
​【数据结构与算法】冒泡排序:简单易懂的排序算法解析
​【数据结构与算法】冒泡排序:简单易懂的排序算法解析
|
7月前
|
搜索推荐 算法 数据处理
从程序设计的角度探索排序算法:冒泡排序的实现与优化
从程序设计的角度探索排序算法:冒泡排序的实现与优化
48 0
|
7月前
|
搜索推荐 算法 程序员
排序算法探秘:打造通用qsort函数
排序算法探秘:打造通用qsort函数
41 0
|
7月前
|
搜索推荐 算法
常见排序算法以及冒泡排序的基础使用方法
常见排序算法以及冒泡排序的基础使用方法
48 0
|
搜索推荐 算法 C语言
【数据结构】—从冒泡排序丝滑过度快速排序(含C语言实现)
【数据结构】—从冒泡排序丝滑过度快速排序(含C语言实现)
【数据结构】—从冒泡排序丝滑过度快速排序(含C语言实现)
|
算法 搜索推荐
C进阶:指针(2),qsort函数,模拟实现冒泡算法(下)
C进阶:指针(2),qsort函数,模拟实现冒泡算法(下)
80 0
|
算法
C进阶:指针(2),qsort函数,模拟实现冒泡算法(上)
C进阶:指针(2),qsort函数,模拟实现冒泡算法
78 0
|
C语言 C++
冒泡排序模拟qsort函数
冒泡排序模拟qsort函数
115 0
冒泡排序模拟qsort函数