【问题标题】:Ten most frequent characters and their frequencies十个最常见的字符及其频率
【发布时间】:2021-01-23 14:52:57
【问题描述】:

这是我的任务:

编写一个程序,要求用户输入文件名。程序以二进制模式打开文件并计算所有字符的频率 [0-255] 并打印出十个最常见字符及其频率的列表。

这是我已经写的大部分代码:

#pragma warning(disable: 4996)

#include <stdio.h>

int count[26];

int main() {
    FILE *f;
    int i;
    char ch;
    char filename[80];

    printf("Enter File name\n");
    gets(filename);
    f = fopen("file.txt", "rb");

    while (!feof(f)) {
        ch = fgetc(f);
        count[ch - 'a']++;
    }
    for (i = 0; i < 26; i++)
        printf("[%c] = %d times\n", 65 + i, count[i]);

    fclose(f);

    return 0;
} 

我能够计算和打印所有字符的频率。如何只打印最频繁的十个?

【问题讨论】:

  • 顺便说一下,您对while (!feof(f)) 的使用是wrong,它将导致我们对数组count 的范围访问。您应该使用“读取”的内容之前检查读取是否成功。
  • 1.创建一个具有字符和频率的结构数组 2. 根据频率降序对数组进行排序 3. 打印数组的前 10 个元素
  • 如果数据不在'a''z' 范围内,您将破坏数组。而且很可能'\n''j' 更频繁。

标签: c


【解决方案1】:

一个简单的解决方案是在最后对频率进行排序,尽管我确信使用高级数据结构可以更优化地解决它。我将提供一个简单而幼稚的解决方案。

维护两个大小为 256 的数组。一个将保存字符(a、b、c 等),另一个将保存相应的频率。字符数组将被初始化一次,同时计算频率(类似于您已经完成的操作)。

在第二阶段,您使用冒泡排序、插入排序等算法对频率数组进行排序。当您移动频率时,还要移动相应的字符以使映射保持不变。

排序后,你可以取前 N 个频率,字符数组会告诉你实际的字符。

【讨论】:

    【解决方案2】:

    您可以遍历 count 数组 10 次以获得最大值(以及它们的位置,这样您就不会一次又一次地记录相同的最大值)。如果您的程序计划使用“10”个最常见的字符运行,那么在这种方法中,您将具有 O(10*length of count array) 的时间复杂度,这最终是恒定的。

    但是,如果您要将 '10' 更改为 'n',那么您可能需要考虑构建在自平衡二叉搜索树上的地图之类的东西。

    【讨论】:

      【解决方案3】:

      您的代码包含几个缺陷。首先,while(!feof(file)) is always wrong

      那么,永远不要使用gets()It's dangerous

      那么我们有 count 不能取所有值,因为它只有 26 大。它的声明应该是 char count[256]; 并且没有理由在 main 之外声明它。好吧,这里唯一的原因是您不必将其初始化为零,但这不是使用全局变量的好理由。

      由于这是一项显而易见的作业,因此我不会为您提供完整的解决方案。但我会做这样的事情:

      struct count_entry { // Yeah, bad name but whatever
          unsigned char c;
          int n;
      };
      
      struct count_entry count[256];
      for(int i=0; i<256; i++) {
          count[i] = (struct count_entry){.c=i, .n=0};
      }
      
      FILE *in = fopen(filename, "rb");
      
      int ch; // Yes, should be int
      
      while ((ch = fgetc(in)) != EOF) 
          count[ch].n++;
      

      然后我要做的是将countqsort 进行排序。阅读文档以了解如何操作。看起来你可以这样做,但我没有仔细阅读:How to sort struct using qsort

      然后您只需像这样打印前 10 个:

      for(int i=0; i<10; i++) 
          printf("%u %d\n", count[i].c, count[i].n);
      

      【讨论】:

        【解决方案4】:

        代码中存在多个问题:

        • gets() 是一个安全漏洞,请勿使用此功能。请改用scanf()fgets()
        • 您将文件名读入filename,但使用"file.txt"
        • 您没有测试fopen() 失败。
        • while (!feof(f))... 不正确。
        • 您增加count[ch - 65] 而不测试字节是否确实是大写字母。

        她是修改版:

        #include <stdio.h>
        
        int main() {
            int count[256] = { 0 };
            char filename[80];
            FILE *f;
            int ch;
        
            printf("Enter File name\n");
            if (scanf("%79[^\n]", filename) != 1)
                return 1;
        
            f = fopen(filename, "rb");
            if (f == NULL) {
                fprintf(stderr, "cannot open %s\n", filename);
                return 1;
            }
            while ((ch = fgetc(f)) != EOF) {
                count[ch]++;
            }
            fclose(f);
        
            for (int n = 0; n < 10; n++) {
                int maxc = 0;
                for (int i = 1; i < 256; i++) {
                    if (count[i] > count[maxc])
                        maxc = i;
                }
                if (count[maxc] != 0) {
                    printf("[%c] = %d times\n", maxc, count[maxc]);
                    count[maxc] = 0;
                } else {
                    break;
                }
            }
            return 0;
        }
        

        【讨论】:

        • 完美,谢谢。你已经帮助了我,但如果我能问最后一个问题。这条线有什么作用? if (count[i] > count[maxc]) maxc = i;
        • 测试检查循环count[i]中当前字节值的计数是否大于当前持有最大计数的字节计数。如果更大,我设置maxc = i,也就是说我们有一个新记录。循环完成后,我打印最大计数和相应的字节,如果它不是 0,如果文件为空,就会发生这种情况。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-06-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多