使用char数组在C中进行Quicksort

时间:2014-07-15 07:01:45

标签: c quicksort

我正在尝试在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;
}

你可能会看到我在这里完全不知所措。非常感谢任何帮助。

3 个答案:

答案 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以下的项目在任何时候都小于数据透视值,indexi之间的所有项都大于或等于数据透视值。这非常狡猾且看似简单。它有两个问题:

  1. indexi相同时,值为&lt;枢轴值,然后它自己交换自己。如果交换费用昂贵,那么“自我交换”测试可能会节省一些。

  2. 当它交换时,它会将index向前的值移动到i,并踩到两者。如果index到达该值,则可能必须再次向前交换。因此,它可能会在推进的“指数”之前进行额外的交换以改变价值。

  3. 考虑五个值:9 3 5 1 4 - 5将被选为枢轴值,并与4交换,给出9 3 4 1 5。从index == 0i == 0开始。 9大于枢轴,因此请离开index并前进i。现在3小于枢轴,所以我们交换9和3并推进indexi,得到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充满了敬意,但他们的分区功能还是打破了大块。