按C中的流行度排序字符

时间:2017-01-21 04:33:15

标签: c sorting

我编写了一个代码,用于列出字符串中字符的出现次数。它有效,但我想知道,是否有可能把它整理好?例如下降。听起来很简单,但我在这里使用了两个数组,我不知道怎么可能将它们链接起来,这样它们就不会在排序后搞砸了。我读到在C ++中我可以使用std :: pair但是从我发现的内容来看,在C中没有替代它。

有没有一种简单的方法可以对它进行排序,以便我可以列出大多数/最不受欢迎的字符?我开始进入冒泡排序,但无论我选择什么,两个阵列之间仍然没有链接。

以下是代码:

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

main(int argc, char **argv) {
    int hits[26] = { 0 };
    char letters[26] = {
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
        'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
        'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
    char *line;
    int i;

    printf("Write the line:\n");
    scanf("%25[^\n]", line);
    for (i = 0; i < strlen(line); i++) {
        if (!isalpha(line[i]))
            continue;
        hits[(int)(tolower(line[i]) - 'a')]++;
    }

    for (i = 0; i < 26; i++) {
        printf("%c is showing up %d times.\n", letters[i], hits[i]);
    }
    return 0;
}

也许在我的方法中有一个简单的错误,如果是的话,那么我很乐意指出它。

4 个答案:

答案 0 :(得分:3)

你使用指针line而没有初始化它 - UB。

你应该为它动态分配,或者只是使用带有适当配置的静态数组 - 例如,1000个字符(包括'\ 0')就足够了。

char line[1000] = "";

你真的不需要将输入行限制为25个字符,如下所示:

scanf("%25[^\n]", line);

相反,使用fgets会更好:

fgets(line, sizeof(line), stdin);

顺便说一句,你真的不需要演员:

hits[tolower(line[i]) - 'a')]++;

然后你的解决方案应该有效。

答案 1 :(得分:3)

首先,在@ artm的回答中提出修正建议。

但是关于你的排序问题。 C标准库具有一个(或多个,取决于版本)例程,用于对任何类型的数组进行排序。最常见的是qsort(3)。问题是,您必须为例程提供一个可用于比较数组元素的函数。因此,您需要一种将字符及其出现一起存储的方法,并且您需要编写该比较函数。

我建议接受@ jack的评论并使用一系列结构。每个结构都类似于:

struct letter {
    char character;
    int count;
};

将字符及其出现次数分组。然后分配一个这样的数组,每个字母一个(而不是现在只有int[])。

struct letter letters[26];
for (int i = 0; i < 26; i++) {
    letters[i] = (struct letter){ (char)(i + 'a'), 0 };
}

在搜索从用户检索到的行的循环中,每次看到相应字母时都会更新letter.count字段。

然后,您可以使用标准库qsort(3)函数根据其中的计数对字母数组进行排序。要使用该函数,您需要一个比较函数,它告诉库两个元素的排序(更大或更小)。

比较函数必须具有签名:int (*compar)(const void *, const void *)void*指向letters数组中的各个元素,因此您需要将它们转换为struct letter并比较其中的计数。它可能看起来像这样(未经测试):

int letter_cmp(const void* first, const void* second) {
    struct letter* first_letter = (struct letter *) first;
    struct letter* second_letter = (struct letter*) second;
    if (first_letter->count == second_letter->count) {
        return 0;
    } else if (first_letter->count < second_letter->count) {
        return -1;
    }
    return 1;
}

然后按照以下方式调用qort(3)函数。

qsort(&letters[0], sizeof(letters), sizeof(struct letter), &letter_cmp);

第一个参数是数组的开头。第二个是元素中数组的大小。第三个是每个元素的大小,第四个是你的比较函数。

此调用返回后,letters数组已按增加count的顺序排序。因此,letters[0].character会为您提供最低count的字符,而letters[25].character会为您提供最高(最多次出现)的字符。

答案 2 :(得分:1)

可以根据需要使用和分配指向结构的指针。指针可以类似于数组使用,但可以使用realloc进行扩展。这将允许包含大写和小写字母或任何char所需的范围 这会从stdin中获取字符,直到EOF或换行符,因此对输入的字符数没有太大的限制。

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

struct charcount {
    char ch;
    int count;
};

void showeach ( struct charcount *ary, int size) {
    int index = 0;
    int ch = 0;

    for ( index = 0; index < size; index++) {
        if ( index % 3 == 0) {
            printf ( "\n");
        }
        if ( index && index % 48 == 0) {
            printf ( "\npress enter\n");
            while ( ( ch = fgetc ( stdin)) != '\n') {
                if ( ch == EOF) {
                    fprintf ( stderr, "problem getting input. EOF\n");
                }
            }
        }
        //show the characters and the count
        printf ( " %4c", ary[index].ch);

        printf ( "%6d      "
        , ary[index].count);
    }
    printf ( "\n");
}

int main(void)
{
    size_t index = 0;
    size_t elements = 0;
    size_t loop = 0;
    int ch = 0;
    int found = 0;
    struct charcount *chno = NULL;//so realloc will work on first call
    struct charcount *temp = NULL;
    struct charcount swap;

    //get each character until newline or EOF
    while ( ( ( ch = fgetc ( stdin)) != EOF) && ch != '\n') {
        if ( ch && isalpha ( ch)) {//restict to letters
            ch = tolower ( ch);//restrict to lower case
            found = 0;
            for ( loop = 0; loop < elements; loop++) {
                if ( ch == chno[loop].ch) {
                    chno[loop].count++;
                    found = 1;
                    break;
                }
            }
            if ( !found) {//new element. reallocate
                if ( ( temp = realloc ( chno, sizeof ( *chno) * (elements + 1))) == NULL) {
                    fprintf ( stderr, "problem allocating\n");
                    free ( chno);
                    return 1;
                }
                chno = temp;
                chno[elements].ch = ch;
                chno[elements].count = 1;
                //sort by letter
                for ( loop = elements; loop > 0; loop--) {
                    if ( chno[loop].ch < chno[loop - 1].ch) {
                        swap = chno[loop];
                        chno[loop] = chno[loop - 1];
                        chno[loop - 1] = swap;
                        continue;
                    }
                    break;
                }
                elements++;
            }
        }
    }

    showeach ( chno, elements);

    //sort by count
    for ( index = 1; index < elements; index++) {
        for ( loop = index; loop > 0; loop--) {
            if ( chno[loop].count > chno[loop - 1].count) {
                swap = chno[loop];
                chno[loop] = chno[loop - 1];
                chno[loop - 1] = swap;
            }
            else {
                break;
            }
        }
    }

    showeach ( chno, elements);

    if ( chno) {
        free ( chno);//free allocated memory
    }
    return 0;
}

答案 3 :(得分:1)

您的程序崩溃是因为您使用line来读取scanf()的字节,但它是一个未初始化的指针,因此将字节存储到它会调用未定义的行为。

  • 您可以将line定义为数组来解决此问题:char line[80];
  • 或者,您可以修改代码以读取任意长行,一次一个字节getchar()

要按递减顺序打印统计数据,您可以使用结构数组并对其进行排序,或者只是迭代地打印最大数字并重置它:

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

main(int argc, char **argv) {
    int hits['z' - 'a' + 1] = { 0 };
    int c, i, max;

    printf("Write the line:\n");
    while ((c = getchar()) != EOF && c != '\n') {
        if (isalpha(c)) {
            hits[tolower(c) - 'a'] += 1;
        }
    }

    for (;;) {
        for (i = max = 0; i < 'z' - 'a' + 1; i++) {
            if (hits[i] > hits[max])
                max = i;
        }
        if (hits[max] == 0)
            break;
        printf("%c is showing up %d times.\n", 'a' + max, hits[max]);
        hits[max] = 0;
    }
    return 0;
}