按字典顺序对整数数组进行排序

时间:2017-01-08 00:58:29

标签: c pointers lexicographic

我使用qsort()以缩放方式对整数的二维数组进行排序,每行都是一个输入,但在遍历数组中的行时遇到指针运算问题。

想法是将一行中的每一列连接成一个字符串,并将strcmp与其他字符串计算得类似。

输入仅限于正整数。以下是一些例子,
{1, 1, 10}之前{1, 2, 0} {3, 1, 5}之前{3, 11, 5} {1, 19, 2}之前的{1, 2, 1}

#define COL_SIZE 3  

int cmp_func (const void *a, const void *b) {
    char str1[100], str2[100], temp[10];
    int i;
    const int **ia = (const int **)a;
    const int **ib = (const int **)b;

    printf("(a: %p), (b: %p)\n", a, b);
    str1[0] = '\0';
    str2[0] = '\0';

    for (i = 0; i < COL_SIZE; i++) {
        printf("(ia+%d: %p), (ib+%d: %p)\n", i, (ia + i), i, (ib + i));
        sprintf(temp, "%d", (int)*(ia + i));
        strcat(str1, temp);

        sprintf(temp, "%d", (int)*(ib + i));
        strcat(str2, temp);
    }

    printf("a: %s, b:%s\n", str1, str2);
    return(strcmp(str1, str2));
}

int main (void) {
    int i, len;
    int A[][COL_SIZE] = {{1,2,3},
                         {1,1,5},
                         {1,0,1},
                         {1,2,1},
                         {1,2,0}};

    len = sizeof(A)/sizeof(A[0]);
    printf("sizeof(int): %ld, sizeof(int *): %ld\n", sizeof(int), sizeof(int *));

    for (i = 0; i < len; i++) {
        printf("Address of A[%d][0]: %p\n", i, A[i]);
    }

    qsort(A, len, COL_SIZE * sizeof(int *), cmp_func);
    return 0;
}

以下是输出:

sizeof(int): 4, sizeof(int *): 8  
Address of A[0][0]: 0x7fff58e9fb30  
Address of A[1][0]: 0x7fff58e9fb3c  
Address of A[2][0]: 0x7fff58e9fb48
Address of A[3][0]: 0x7fff58e9fb54  
Address of A[4][0]: 0x7fff58e9fb60  
(a: 0x7fff58e9fb30), (b: 0x7fff58e9fb48)  
(ia+0: 0x7fff58e9fb30), (ib+0: 0x7fff58e9fb48)  
(ia+1: 0x7fff58e9fb38), (ib+1: 0x7fff58e9fb50)  
(ia+2: 0x7fff58e9fb40), (ib+2: 0x7fff58e9fb58)  
a: 131, b:112  
(a: 0x7fff58e9fb48), (b: 0x7fff58e9fb60)  
(ia+0: 0x7fff58e9fb48), (ib+0: 0x7fff58e9fb60)  
(ia+1: 0x7fff58e9fb50), (ib+1: 0x7fff58e9fb68)  
(ia+2: 0x7fff58e9fb58), (ib+2: 0x7fff58e9fb70)  
Abort trap: 6

*(ia + 1)的指针算法导致每次迭代中的地址从(sizeof(int *))跳过0x7fff58e9fb300x7fff58e9fb38,而A [0]中的下一个值存储在0x7fff58e9fb34(int的大小)。

如何解决此问题,以便我可以获得4而不是8的偏移?

4 个答案:

答案 0 :(得分:2)

您正在排序数组数组,而不是指针数组。因此,您需要清理比较函数中的代码。此外,比较器的当前化身在进行任何比较之前将所有数字转换为字符串。在每一行中一次转换一个数字并在找到差异时停止,只有在前一个数字相同时转换下一个数字才是可行和明智的。

这导致了以下代码,它需要C99编译器或支持VLA的C11编译器 - 可变长度数组 - 这在C99中是强制性的,但在C11中是可选的:

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

static void dump_array(const char *tag, size_t n_rows, size_t n_cols, int A[n_rows][n_cols]);

enum { COLS  = 3 };

extern int cmp_func (const void *a, const void *b);

int cmp_func(const void *va, const void *vb)
{
    const int *a = *(const int (*)[COLS])va;
    const int *b = *(const int (*)[COLS])vb;

    for (int i = 0; i < COLS; i++)
    {
        char str1[20], str2[20];
        sprintf(str1, "%d", a[i]);
        sprintf(str2, "%d", b[i]);
        int rc = strcmp(str1, str2);
        if (rc != 0)
            return rc;
    }
    return 0;
}

int main(void)
{
    int A[][COLS] =
    {
        { 1, 91, 10 },
        { 1,  9,  9 },
        { 1,  9, 11 },
        { 1,  1,  5 },
        { 1,  9, 10 },
        { 1,  0,  1 },
        { 1,  2,  3 },
        { 1, 91, 10 },
        { 1, 19,  0 },
        { 1, 91,  0 },
        { 1,  2,  0 },
        { 1,  2,  1 },
    };
    enum { ROWS = sizeof(A) / sizeof(A[0]) };

    dump_array("Before", ROWS, COLS, A);
    qsort(A, ROWS, sizeof(A[0]), cmp_func);
    dump_array("After", ROWS, COLS, A);
    return 0;
}

static void dump_array(const char *tag, size_t n_rows, size_t n_cols, int A[n_rows][n_cols])
{
    printf("%s:\n", tag);
    for (size_t r = 0; r < n_rows; r++)
    {
        const char *pad = "{";
        for (size_t c = 0; c < n_cols; c++)
        {
            printf("%s%3d", pad, A[r][c]);
            pad = ",";
        }
        puts(" }");
    }
}

传递给cmp_func()的指针是指向COLS int s或int (*)[COLS]数组的指针; ab的分配会生成一个数组int [COLS],它会衰减到int *。我通常使用v作为比较器的const void *参数的前缀,并删除函数中工作变量的v

测试数据包括评论中的测试值,输出为:

Before:
{  1, 91, 10 }
{  1,  9,  9 }
{  1,  9, 11 }
{  1,  1,  5 }
{  1,  9, 10 }
{  1,  0,  1 }
{  1,  2,  3 }
{  1, 91, 10 }
{  1, 19,  0 }
{  1, 91,  0 }
{  1,  2,  0 }
{  1,  2,  1 }
After:
{  1,  0,  1 }
{  1,  1,  5 }
{  1, 19,  0 }
{  1,  2,  0 }
{  1,  2,  1 }
{  1,  2,  3 }
{  1,  9, 10 }
{  1,  9, 11 }
{  1,  9,  9 }
{  1, 91,  0 }
{  1, 91, 10 }
{  1, 91, 10 }

此代码类似于code RoadRunner,但主要区别在于更简单,更有效的比较函数cmp_func(),它不一定将每行中的所有数字转换为字符串每次比较两行。 (在示例数据中,第一个值始终为1,因此至少会转换两对数字。但这可能不正常。)

答案 1 :(得分:1)

我不认为连接比较行的整数是一个好主意。通常,结果字符串可以具有不同的大小。至少你必须为每个转换后的数字添加前导零,以便对齐字符串并考虑整数的符号。

你也错误地取消引用了空指针。

qsort的来电不正确,但如果sizeof( int * )等于sizeof( int )则可以有效。你应该写

qsort(A, len, sizeof( int[COL_SIZE] ), cmp_func);

我会按以下方式编写程序

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

#define COL_SIZE 3

int cmp_rows(const void *a, const void *b)
{
    const int *left  = *(const int(*)[COL_SIZE])a;
    const int *right = *(const int(*)[COL_SIZE])b;

    for (size_t i = 0; i < COL_SIZE; i++)
    {
        if ( left[i] < right[i]) return -1;
        else if (right[i] < left[i]) return 1;
    }

    return 0;
}

int main( void )
{
    int A[][COL_SIZE] = 
    { 
        { 1,2,3 },
        { 1,1,5 },
        { 1,0,1 },
        { 1,2,1 },
        { 1,2,0 } 
    };

    qsort(A, sizeof(A) / sizeof(*A), sizeof(int[COL_SIZE]), cmp_rows);

    for (size_t i = 0; i < sizeof(A) / sizeof(*A); i++)
    {
        for (size_t j = 0; j < COL_SIZE; j++) printf("%d ", A[i][j]);
        printf("\n");
    }

    return 0;
}

程序输出

1 0 1
1 1 5
1 2 0
1 2 1
1 2 3

答案 2 :(得分:1)

在这两行中:

const int **ia = (const int **)a;
const int **ib = (const int **)b;

这是不正确的,您只需要一个*指针,因为您要将每行中的元素与其他行进行比较。

这需要更改为:

const int *ia = (const int *)a;
const int *ib = (const int *)b;

此外,这一行:

qsort(A, len, COL_SIZE * sizeof(int *), cmp_func);

需要更改为:

qsort(A, len, sizeof(*A), cmp_func);

qsort中的第三个参数只需要比较它的大小。在这种情况下,每行的大小,可以表示为sizeof(*A)sizeof(A[0])

此外,来自莫斯科的@Vlad有一个更好的方法,不需要连接字符串。

如果你仍然希望使用字符串方法,你可以试试这个:

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

#define STRSIZE 100
#define COL_SIZE 3

int cmp_func(const void *a, const void *b) {
    char str1[STRSIZE], str2[STRSIZE], temp[STRSIZE];
    size_t i;

    const int *row1 = (const int *)a;
    const int *row2 = (const int *)b;

    *str1 = '\0';
    *str2 = '\0';

    for (i = 0; i < COL_SIZE; i++) {
        sprintf(temp, "%d", row1[i]);
        strcat(str1, temp);

        sprintf(temp, "%d", row2[i]);
        strcat(str2, temp);
    }  

    return strcmp(str1, str2);

}

void print_array(int A[][COL_SIZE], size_t len) {
    size_t row, col;

    for (row = 0; row < len; row++) {
        for (col = 0; col < COL_SIZE; col++) {
            printf("%d ", A[row][col]);
        }
        printf("\n");
    }
}

int main(void) {
    size_t len;
    int A[][COL_SIZE] = {{1,2,0},
                         {1,19,0},
                         {1,19,0},
                         {1,19,0},
                         {1,2,0}};

    len = sizeof(A)/sizeof(*A);

    printf("\nBefore:\n");
    print_array(A, len);

    qsort(A, len, sizeof(*A), cmp_func);

    printf("\nAfter:\n");
    print_array(A, len);

    return 0;
}

输出:

Before:
1 2 0
1 19 0
1 19 0
1 19 0
1 2 0

After:
1 19 0
1 19 0
1 19 0
1 2 0
1 2 0

答案 3 :(得分:0)

问题在于:

qsort(A, len, COL_SIZE * sizeof(int *), cmp_func);

你告诉qsort()要排序&#39; A&#39 ;,它有&#39; len&#39;(5)项,每个项目都是3 * int的大。但是,&#39; A&#39;是由int组成的,而不是* int&#39; s。

qsort(A, len, COL_SIZE * sizeof(int), cmp_func);

试一试。