我正在尝试在C排序char数组中实现quicksort。我正在使用递归方法,也可以在维基百科上找到。这是相关的代码:
#define TSIZE 32
#define TNUM 100000
交换宏:
#define SWAP(a, b, size) \
do \
{ \
size_t __size = (size); \
char *__a = (a), *__b = (b); \
do \
{ \
char __tmp = *__a; \
*__a++ = *__b; \
*__b++ = __tmp; \
} while (--__size > 0); \
} while (0)
此交换宏取自已在C中实现的qsort函数。
char数组:
char TWEETS[TNUM * TSIZE];
我省略了我将所有数据读入char数组的部分,因为我在这里的设置与qsort()函数完美配合。然而,这里是一个关于如何读取数据的摘录:
0 0 Feb 09 @RafinhaCosgrove OIE VOLTEI <33
0 1 Feb 19 @Nha_RDO tanyak ajah , taik idup dia!
0 2 Mar 08 @w0nderlxss No Hi's Mine
最初的快速呼叫:
quicksort(TWEETS, 0, TNUM-1, compare);
比较功能:
int compare(const void* ptr1, const void* ptr2) {
int i;
unsigned char * t1 = (unsigned char *) ptr1;
unsigned char * t2 = (unsigned char *) ptr2;
for (i = 6; i < TSIZE; i++) {
if (t1[i] > t2[i])
return -1;
if (t2[i] > t1[i])
return 1;
}
return 0;
}
快速排序功能:
void quicksort(void* base, int left, int right, int (*compar)(const void* , const void*))
{
if(left < right) {
int pivot = partition(base, left, right, compar);
quicksort(base, left, pivot-1, compar);
quicksort(base, pivot+1, right, compar);
}
}
最后是分区功能:
int partition(void* arr, int left, int right, int (*compar)(const void* , const void*))
{
char* cArr = (char*) arr;
int i;
int pivotIndex = (left+right)/2;
char* pivotValue = &cArr[TSIZE * pivotIndex];
int index = left;
SWAP(&cArr[TSIZE * pivotIndex], &cArr[TSIZE * right], TSIZE);
for(i = left; i < right; i++) {
if(compar((void*) &cArr[TSIZE * i], (void*) pivotValue) < 0) {
SWAP(&cArr[TSIZE * i], &cArr[TSIZE * index], TSIZE);
index++;
}
}
SWAP(&cArr[TSIZE * index], &cArr[TSIZE * right], TSIZE);
return index;
}
现在你应该注意一些事情:
1)代码设置在使用int数组和几个数字而不是char数组时起作用
2)只需使用qsort()函数,代码设置(读入数据等)就可以工作。我也使用它的结果作为我自己实现的输出的比较
3)因为它与qsort()一起使用,所以比较函数不应该是错误的
4)因为它与qsort()一起使用,并且交换宏来自qsort()实现,所以它也不应该是错误的。
为了完整起见,这里是使用int数组而不是char数组时的相关代码部分(再一次,工作)。
调用主要功能:
int array[15] = {9, 6, 2, 0, 3, 1, 4, 8, 5, 7, 7, 50, 132, 12, 45};
quicksort(array, 0, 14, compare);
交换&amp;分区功能:
void swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int partition(int* arr, int left, int right, int (*compar)(const void* , const void*))
{
int i;
int pivotIndex = (left+right)/2;
int pivotValue = arr[pivotIndex];
int index = left;
swap(&arr[pivotIndex], &arr[right]);
for(i = left; i < right; i++) {
if(arr[i] < pivotValue) {
swap(&arr[i], &arr[index]);
index++;
}
}
swap(&arr[index], &arr[right]);
return index;
}
你可能会看到我在这里完全不知所措。非常感谢任何帮助。
答案 0 :(得分:1)
在partition()
中,更改
char* pivotValue = &cArr[TSIZE * pivotIndex];
到
char* pivotValue = &cArr[TSIZE * right];
以后
SWAP(&cArr[TSIZE * pivotIndex], &cArr[TSIZE * right], TSIZE);
枢轴元素位于right
,不再位于pivotIndex
。
你的算法正在向后排序。如果那不是您想要的,请更改compare()
的符号。
答案 1 :(得分:0)
我发现该程序的行为符合编码。在分区调用中,存在一个类型转换的问题,需要从void *到int *的显式转换,程序的其余部分与您的示例数据一起使用。输出是: 0 1 2 3 4 5 6 7 7 8 9 12 45 50 132
#include <iostream>
#include<vector>
using namespace std;
int compare(const int* ptr1, const int* ptr2) {
if (*ptr1 > *ptr2)
return -1;
else if (*ptr1 > *ptr2)
return 1;
return 0;
}
void swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int partition(int* arr, int left, int right, int (*compar)(const int* , const int*))
{
int i;
int pivotIndex = (left+right)/2;
int pivotValue = arr[pivotIndex];
int index = left;
swap(&arr[pivotIndex], &arr[right]);
for(i = left; i < right; i++) {
if(arr[i] < pivotValue) {
swap(&arr[i], &arr[index]);
index++;
}
}
swap(&arr[index], &arr[right]);
return index;
}
void quicksort(void* base, int left, int right, int (*compar)(const int* , const int*))
{
if(left < right) {
int pivot = partition((int *)base, left, right, compar);
quicksort(base, left, pivot-1, compar);
quicksort(base, pivot+1, right, compar);
}
}
int main( )
{
int array[15] = {9, 6, 2, 0, 3, 1, 4, 8, 5, 7, 7, 50, 132, 12, 45};
quicksort(array, 0, 14, compare);
for (int i = 0; i < 15; i++) {
cout << array[i] << " " ;
}
cout << endl;
return 0;
}
答案 2 :(得分:0)
令我惊讶的是分区功能似乎有效。 index
以下的项目在任何时候都小于数据透视值,index
和i
之间的所有项都大于或等于数据透视值。这非常狡猾且看似简单。它有两个问题:
当index
和i
相同时,值为&lt;枢轴值,然后它自己交换自己。如果交换费用昂贵,那么“自我交换”测试可能会节省一些。
当它交换时,它会将index
向前的值移动到i
,并踩到两者。如果index
到达该值,则可能必须再次向前交换。因此,它可能会在推进的“指数”之前进行额外的交换以改变价值。
考虑五个值:9 3 5 1 4
- 5将被选为枢轴值,并与4交换,给出9 3 4 1 5
。从index == 0
和i == 0
开始。 9大于枢轴,因此请离开index
并前进i
。现在3小于枢轴,所以我们交换9和3并推进index
和i
,得到3 9 4 1 5
。现在4小于枢轴,所以再次交换,改组9前锋,给3 4 9 1 5
。并且1也小于枢轴,因此再次交换,给出3 4 1 9 5
。将透视值交换到位可以完成提供3 4 1 5 9
的过程。
所以,这会进行3次交换,以便对9进行随机播放,只需要进行一次交换。
执行分区的常用方法是从左侧和右侧进行操作,查找需要上下交换的值,以便以最小数量的交换完成该过程。
我尝试了50个整数的向量,每个值从1..50随机选择。我运行了“维基百科”分区和一个更“传统”的分区,并计算了超过20,000次试验的掉期。我得到了:
Average 'trad' swaps = 9.8
Average 'wiki' swaps = 23.0
Average 'wiki' selfs = 2.9
Average 'wiki' extra = 13.0
其中'trad'最小化掉期数量。 “自我”是交换,其中index
== i
,而extras
是价值不止一次交换的地方 - 毫不奇怪,'wiki'交换了较少的'wiki'额外内容与'trad'掉期差不多。
尽管对Messrs Wikipedia和Sons充满了敬意,但他们的分区功能还是打破了大块。