【问题标题】:C how to use quicksort with comparator function [duplicate]C如何使用带有比较器功能的快速排序[重复]
【发布时间】:2017-09-18 00:43:54
【问题描述】:

我试图了解这个比较器函数stringAsInt(const void *pLeft, const void *pRight)的前几行发生了什么

  1. 所以参数是一些东西的常量指针。然后在接下来的两行中,我们将 void 转换为 (const char**) 为什么将其转换为指针的指针?另外,这两行到底是怎么回事?
  2. main()函数中调用qsort()时,为什么没有参数传给stringAsInt()stringAsInt() 怎么知道 pLeftpRight 是什么?
  3. 为什么a被设置为指针的指针?一个标准的数组还不够吗?

-

int stringAsInt(const void *pLeft, const void *pRight) {
    const char *left = *(const char**)pLeft;
    const char *right = *(const char**)pRight;
    int leftLen = (int)strlen(left);
    int rightLen = (int)strlen(right);
    if (leftLen != rightLen) {
        return leftLen - rightLen;
    } else {
        return strcmp(left, right);
    }
}

int main() {
    int n;
    scanf("%d", &n);
    char buffer[1000000 + 1];
    char **a = malloc(sizeof(char*) * (size_t)n);
    for (int i = 0; i < n; i++) {
        scanf("%1000000s", buffer);
        a[i] = malloc(sizeof(char) * (strlen(buffer) + 1));
        strcpy(a[i], buffer);
    }
    qsort(a, (size_t)n, sizeof(a[0]), stringAsInt);
    for (int i = 0; i < n; i++) {
        printf("%s\n", a[i]);
        free(a[i]);
    }
    free(a);
    return 0;
} 

【问题讨论】:

  • char buffer[1000000 + 1]; 是真的吗?
  • @Brandon - 是的,这是 Hackerrank 的一个问题。所以我最初想实现一个二叉搜索树,但它一直超时。 hackerrank.com/challenges/big-sorting/problem
  • 有无数个重复项。你有 6687 名代表,我想你现在应该知道要先搜索他​​们。
  • 至于3,是你写的。你本可以使用char *a[],但决定使用malloc

标签: c pointers quicksort qsort


【解决方案1】:

cmets 中的代码分解。

//
//  main.c
//

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//Takes a pointer to any type.. (void *)
int stringAsInt(const void *pLeft, const void *pRight) {
    //qsort calls this function with a pointer to each element. Since each element is a char* then pLeft is actually a char**.
    //For example, if sorting an array of ints (each element is of type: int), pLeft would be int*.
    //If sorting an array of strings (each element is of type: char*), pLeft would be char**.
    //So on and so forth.


    //Thus we cast the pLeft to its correct type (const char**) for pointer to string.. Then we dereference it to get the string itself (const char*).
    const char *left = *(const char**)pLeft;
    const char *right = *(const char**)pRight;

    int leftLen = (int)strlen(left);
    int rightLen = (int)strlen(right);

    //Compare the lengths of the strings.. If left is < right, we return negative. If they are the same, 0.. else positive..
    if (leftLen != rightLen) {
        return leftLen - rightLen;
    } else {
        return strcmp(left, right);  //Lengths are equal.. compare their contents..
    }
}

int main() {
    int n;

    //First the code takes an array count.. This is the amount of arrays to sort..
    printf("Enter number of strings: ");
    scanf("%d", &n);

    //It allocates a large buffer on the stack to hold entire sentences to sort..
    char buffer[1000000 + 1];

    //Allocates an array of strings..
    char **a = malloc(sizeof(char*) * (size_t)n);

    for (int i = 0; i < n; i++) {
        printf("Enter a string: ");
        scanf("%1000000s", buffer);

        //Store each string in the array..
        a[i] = malloc(sizeof(char) * (strlen(buffer) + 1));
        strcpy(a[i], buffer);
    }

    //Sort the array using stringAsInt comparator..
    //a is the array to sort.
    //n is the amount of elements in the array.
    //sizeof(a[0]) is the size of each element in the array. sizeof(char*).
    //stringAsInt is the comparator function (pointer to function)..

    qsort(a, (size_t)n, sizeof(a[0]), stringAsInt);

    //Print the sorted array and cleanup each element.
    for (int i = 0; i < n; i++) {
        printf("%s\n", a[i]);
        free(a[i]);
    }

    //Cleanup the array itself.
    free(a);
    return 0;
}

【讨论】:

    【解决方案2】:

    所以参数是一些东西的常量指针。然后在接下来的两行中,我们将 void 转换为 (const char**) 为什么将其转换为指针的指针?另外,这两行到底是怎么回事?

    它是 C 中的类型擦除。qsort 不知道数组元素有什么类型,它只知道它们的大小并将单个元素作为类型擦除的指针传递给比较器 void,期望比较器将指针转换为所需的类型.这正是这两行中发生的事情。字符串数组被简单地组织为指向 char 的指针数组(字符串的传统 C 习惯用法)。数组的每个元素都是一个指向字符的指针(实际上,指向连续字符序列中的第一个)。 qsort 将类型擦除的指针传递给那些,比较器将它们向下转换回具体类型。

    在 main() 函数中调用 qsort() 时,为什么没有参数传递给 stringAsInt()? stringAsInt() 怎么知道 pLeft 和 pRight 是什么?

    它是一个指向函数的指针。 qsort 的最后一个参数是一个函数指针。然后它调用此函数来比较各个元素对,每次都提供 pLeft 和 pRight 的值。

    为什么a被设置为指针的指针?标准数组还不够吗?

    如果您确定自己的文件不大,可能。如果它由数百万个字符串组成,由于该站点的 URI 的二级域,程序很可能会崩溃,因此他们决定将整个数组放在堆上。

    【讨论】:

      【解决方案3】:
      1. stringAsInt 中的强制转换舞蹈是因为 qsort() 使用 const void * 参数调用比较器函数。然而,比较器需要引用它们指向的实际结构才能进行比较,因此它会转换为正确类型的 const 指针。

      在这种情况下,正确的类型是指向 char 指针的指针,因为字符串是 char *,而该字符串的地址是 char **。 qsort 将对字符串数组进行排序。

      C 就是这样混淆的。考虑以下包含以下字符的内存地址:

      0x0100 'f'
      0x0101 'o'
      0x0102 'o'
      0x0103 '\0'
      
      0x3127 'b'
      0x3128 'a'
      0x3129 'r'
      0x312a '\0'
      

      我们有一个指向这些字符串的指针数组(上面程序中的变量 a):

      0x2522: 0x0100
      0x2524: 0x3127
      

      所以 a == 0x2522, a[0] = 0x0100 ("foo"), a[1] = 0x3127 ("bar")

      qsort() 将切换 a[0] 和 a[1] 的内容(因为“bar”按字典顺序排列在“foo”之前);调用后的唯一变化是:

      0x2522: 0x3127
      0x2524: 0x0100
      

      字符串本身不会改变。

      1. qsort 例程知道它的第一个元素在哪里,它必须排序多少元素,以及每个元素有多大——它需要知道每个元素的地址(如果数组从 0x1000 开始,有10 个元素,每个元素长 16 个字节,它知道元素 0 在 0x1000,元素 1 在 0x1010,等等)。 qsort 例程将根据需要多次调用 stringAsInt(),并使用它计算的元素,并将交换必须交换的元素以对数组进行排序。

      2. 因为字符串。如果它是对整数进行排序(例如),它将只是一个标准数组。但是字符串是 char * 的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-04-09
        • 1970-01-01
        • 2020-11-20
        • 1970-01-01
        • 1970-01-01
        • 2011-02-11
        • 2011-12-02
        • 2020-10-22
        相关资源
        最近更新 更多