我们正在尝试创建一个冒泡排序,可以接受int
,float或c-string数组。我们已经编写了允许我们这样做的交换函数,但是现在我们不知道如何让它与冒泡排序一起工作。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdbool.h>
void swapG(void* a, void* b, int elemSize)
{
void* temp = (char*)malloc(sizeof(char) * elemSize);
assert(temp != NULL);
memcpy(temp, a, elemSize);
memcpy(a, b, elemSize);
memcpy(b, temp, elemSize);
free(temp);
}
void bubbleSort(void* arr[], int n, int elemSize)
{
int i, j;
bool swapped;
for (i = 0; i < n - 1; i++) {
swapped = false;
for (j = 0; j < n - i - 1; j++) {
if (&arr[j] > &arr[j + 1]) {
swapG(&arr[j], &arr[j + 1], elemSize);
swapped = true;
}
}
if (swapped == false)
break;
}
for (int i = 0; i < n; i++)
printf(&arr[i]);
}
int main()
{
int* arr[] = { 0,1,4,6,9,10,13,7 };
bubbleSort(arr, 8, sizeof(int));
return 0;
}
3条答案
按热度按时间vc9ivgsu1#
为排序函数定义一个函数指针参数,如下所示:
这是一个比较数组中两个元素的函数。你可以这样称呼它:
现在你只需要为每种类型定义不同的函数,当你调用你的排序函数时,像这样传递比较函数名:
avwztpqn2#
这种方法的问题是基础类型没有被抽象。这不仅仅是添加
elemSize
的问题。题外话:camelCase是如此如此的java ;)为什么不只是size
和size_t
?数据可以是
Array
,封装大小和任何有用的元数据,如无论如何,我认为这个问题是一个有趣的问题,我将在下面留下一种方法来做到这一点,因为它是非常常见的需要排序或只是处理数组的不同的东西。
我会写我的代码,所以这将是一个很长的职位,结束了一个完整的程序,排序的3种类型,并显示如何封装行为在
C
使排序的数组排序排序的 * 对象 *。首先,我们需要
bubble_sort
首先需要的是一个有效的冒泡排序实现。让我们试试这个常见的:
这个函数返回为对数组排序而进行的交换次数。这不重要
我们需要测试一下。是否可以不打印数组之前和之后?不。我们希望:数组被排序。
所以上面的8行可以帮助:它显示了数组,并允许一个可选的方便消息,因此在测试过程中不需要对
printf
编写几十个调用。第一个测试,使用
int
和输出
这是伟大的,因为我们几乎没有做什么,有一个功能和测试。
返回
bubble_sort
如果我们想对
int
,float
或char*
进行排序,则使用相同的算法。看看代码,我们需要知道这就是我们所需要的
提取操作
把这些从
bubble_sort()
中弄出来会很难吗?考虑:
好吧,别太用力。返回
bubble_sort
:它像以前一样工作。好
使
swap
和compare
通用因为我们想要对不同的东西进行排序,比如病历、图书馆里的书、票、
int
、字符串,所有常见的学生的东西,让我们将cmp_int和swap改为只使用void*
,没有类型。cmp_int
如果我们有两个
void*
指向原来的int
s...swap_int
如果
swap_int
参数也是void*
,我们可以使用当然,不需要这么多行和变量,但无论如何有6行。
bubble_sort使用新的
swap
和compare
它像以前一样工作。还是很好
但是现在
bubble_sort
不知道它正在排序的数组中的东西的类型。只有swap_int
和cmp_int
是重要的。让我们换个名字:但是向量仍然被声明为
int[]
,这不是泛型。我们开始改变这一切.我们只有
swap
和cmp
。有两个具有类似签名的函数:那么,如果我们将这两个函数作为参数传递给
bubble_sort
呢?C
就是为这类事情而设计的。让我们看看:
bubble_sort()
获取2个新参数现在我们可以将自定义的
cmp
和swap
传递给bubble_sort
,以我们的方式进行排序。更改
main
以提供新参数这只是提供要使用的函数的问题:
输出
是一样的,肯定的。还是很好
我们是否已经对
double
数组进行了排序?当然,这是一回事。但是我们仍然有
bubble_sort
的第一个参数作为int[]
。不好我们还没到那一步。让我们将
bubble_sort()
更改为所以我们可以把数组作为地址和偏移量来操作,毕竟所有的数组都是这样。
C
就是为了处理这种东西而设计的:系统缓冲器、进程间消息、消息队列等。array
是基地址,el_size
用于在array
内存块上进行寻址。剩下的就是我们所拥有的:bubble sort(冒泡排序)bubble_sort()
通用,共20行此版本不知道数组中有什么:
它与前面的代码相同,但
swap
和cmp
是参数,arr
是数组,el_size
是每个元素的大小。在某种意义上,它是一个“对象”:用户需要传递数据和对数据进行操作的方法。
实际上,这只是一个从一种类型复制和粘贴到另一种类型的问题。
实现双精度和字符串的
swap
和compare
就像这样当然,对于
Tickets
,Patient
,无论我们需要操作什么数据,都是一样的。这是OOP,一如既往。这些函数的实现
这就是我们所需要的需要排序新类型?只写
swap
和compare
。抽象
show
方法:打印任何类型这是 java
toString
,C++
<<
override方法.在这个例子中,
而且很有效。但是使用
%d
打印元素,并迭代int[]
数组。不好如果用户可以告诉我们如何显示一个元素,那么我们就可以在
show()
中使用它。* 物体的力量 *show
一个int:7条线路考虑一个函数,如
适用于
int
:只是返回一个表示其值的字符串。但我们可以将其更改为使用double
:复制并粘贴到
show
一个双精度:7条线路复制并粘贴到
show
一个字符串:10行将
show
更改为适用于任何类型:19条线路**这与
bubble_sort
中使用的方法相同:把数据看作一个对象,用户只需要为3个函数提供代码,我们就可以对任何东西进行排序。我们还可以在屏幕上显示任何数据,因为show
获得了一个用户提供的 virtual 函数来显示元素。一个完整的例子:
main.c
这段代码,使用复制和粘贴,声明了一个排序3个数组
int
,double
和char*
。几乎和我们说的一样。此程序的输出:分类所有3种类型
和
printing
功能相同C
完整代码hsgswve43#
不要忽略编译器警告
您似乎忽略了来自编译器的警告。
首先,print语句
无效,因为第一个参数不是格式字符串。
第二,初始化声明:
无效,因为初始化器列表包含
int
,但您正在尝试初始化int*
数组。您的函数只接受
void*
数组在你的问题中,你问:
我们正在尝试创建一个可以接受
int
,float
或c字符串数组的冒泡排序。你的冒泡排序函数有以下原型:
void bubbleSort(void* arr[], int n, int elemSize);
即使你的意图是传递一个指针数组,你的函数也无法完成你想要的任务。虽然指向某种类型
T
的指针可以转换为void *
,反之亦然,但在T*
数组到void*
数组之间没有这种有效的转换。类型不兼容。它的本质是
void*
(和char*
)允许比T*
宽。利用现有解决方案作为模板进行跟踪
有一个原型,你可以模仿做你想做的事情。这就是标准的C函数
qsort
我们注意到这个原型和为冒泡排序生成的原型之间有两个不同之处。首先,数组指针被表示为单个
void*
,而不是数组。其次,要使用的比较函数作为另一个参数传入。第一个区别是允许函数接受任何类型的数组。在C中,表达式中使用的
T
类型数组的对象(除了少数例外)将衰减为具有指向T
的类型指针,其第一个数组成员的地址值。然后,这个指针值自然地转换为标准C所允许的void*
。第二个区别允许你传入不同的比较函数来适应不同类型的数组。因此,
float
数组需要使用与C字符串数组不同的比较函数。因此,您的冒泡排序函数可能更像
qsort
如何使用比较功能
有很多关于如何正确使用
qsort
的资源。但是,简而言之,最棘手的部分是理解如何编写正确的比较函数。不过,唯一的技巧是比较函数的参数是传递给qsort
的数组元素的指针。如果第一个参数小于第二个参数,则比较函数返回负值,如果它们相等,则返回0,否则返回正值。因此,如果sort函数传递了一个
int
s的数组:然后,比较函数可能看起来像这样:
如果sort函数被传递了一个指针数组,那么你需要记住比较函数的每个参数都是指向一个指针的指针。这就是C字符串排序的情况。
所以比较函数的实现可能看起来像这样:
将
void*
视为元素数组要将
arr
参数视为数组,可以使用char*
指针指向第 i_个元素的位置,并进行偏移量计算。如果你的编译器支持VLA扩展,你可以使用它来使索引更自然一些。
改进泛型交换
您实现了一个通用交换,但它有点昂贵。在任何排序算法中,交换操作都应该尽可能高效。但是,您已经将调用
malloc
和free
的开销添加到每个交换中。处理这个问题的一种方法是在排序开始时只为swap临时分配一个空间,并在排序完成时释放它。
现在,您可以修改swap函数以接受临时变量,而不是为自己分配一个临时变量。
为了进一步优化分配,您可以创建一个固定大小的缓冲区用于交换,并且仅在元素大小太大而无法容纳时进行分配。
我们不讨论冒泡排序
冒泡排序算法本身并不是你应该花很多时间学习的东西,所以我不会在这个答案中讨论它的实现细节。
selection sort 怎么样?
一个更有效的排序算法是 * 选择排序 *,它像冒泡排序一样将最大的元素放在最后。选择排序的工作原理是识别感兴趣的子数组中最大的元素,并将其放置到数组的最后一个位置。归纳步骤是将数组大小减少1,停止条件是当数组剩余的元素少于2个时。
N元数组的算法
使用此算法,您仍然可以保持不变,即在收缩数组时对数组的最后一部分进行排序。虽然函数现在命名错误,但算法本身并不那么愚蠢。
* 插入排序 * 怎么样?
该算法的结果是临时不用于交换,而是在定位到感兴趣的元素的正确位置时保持其值。一旦定位,元素将向上移动,为感兴趣的值腾出空间。小数组的数组元素移动被认为是高效的,因为它可以完全在缓存中完成,并且机器通常硬编码优化函数来执行该操作。
N元数组的算法
还有其他方法来描述算法,但上面的代码相当简单:
排序了吗?
在编写排序例程之后,可以使用验证器函数验证它是否已排序。这样的函数只会扫描数组元素,并确保连续的元素彼此小于或等于。