关于#数据结构#的问题,如何解决?

数据结构 获取英文词频

每个英文单词都会从第一个字母累加获取到最后一个字母

img

我不太懂,感觉是单词结束标志部分有问题?

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<time.h>
#define MAX 26                     //26个字母

typedef struct Word              //树的结构体定义
{
    Word* next[MAX];
    int num;                     //确定到这里是不是一个单词(0说明到此不是一个单词,1说明到此是一个单词)
};

typedef struct tlist             //结构体定义:单词和对应频率
{
    char word[200];
    int time;                    //出现次数
};

struct tlist list[3000000];      //定义结构体数组:先定义这么多个结构体对象,即单词种类最多不能超过此数量
Word* root;                         //////字典树的根指针
char str[200] = "";                //字符数据初始化为空
char tempword[1000];             //////每个单词最多不超过1000个字符长度
int size = 0;                      //size为一篇文章中字符串的个数
int all = 0;                     //all为一篇文章中的单词总数
double AllTime = 0;          //总耗时
void output();
void display();
int main();

void CreateWord(char* str)       //新建单词的函数(形参为字符串指针) 
{
    int len = strlen(str);   //strlen函数是测量字符串长度的函数
    int id = 0;
    Word* p = root, * q;

    for (int i = 0; i < len; i++)//遍历单词判断当前字符是否为字母或其他字符
    {
        if (str[i] >= 'a' && str[i] <= 'z')
            id = str[i] - 'a';

        if (str[i] >= 'A' && str[i] <= 'Z')
            id = str[i] - 'A';

        if (p->next[id] == NULL)  //若已到达链表结尾,开辟新的结构体存入字母
        {
            q = (Word*)malloc(sizeof(Word));

            for (int j = 0; j < MAX; j++)
            {
                q->num = 0;
                q->next[j] = NULL;
            }

            p->next[id] = q;        //////前面开辟了一个空间就是给p->next[id]分配空间
            p = p->next[id];
        }
        else                       //若未到达链表结尾,指针指向下一个
        {
            p = p->next[id];
        }
    }
    p->num++;                   //////重复单词数加1
}

void ReadWord(Word* p, int len)     //读单词
{
    int i;
    for (i = 0; i < 26; i++)
    {
        if (p->next[i] != NULL)
        {
            str[len] = 'a' + i;
            len++;

            ReadWord((Word*)p->next[i], len); //递归
            len--;
        }
    }

    if (p->num != 0)
    {
        str[len] = '\0';
        strcpy_s(list[size].word, str);    //如果遇到单词结束标志,将str存入list[size].word
        list[size].time = p->num;
        size++;
    }
}

void output()                           //输出函数
{
    int i;

    FILE* fpx;

    fpx = fopen("D:\\result.txt", "w");        //将统计结果写入到result.txt文件中

    for (i = 1; i < size; i++)

        fprintf(fpx, "%s   %d\n", list[i].word, list[i].time); // 写入所有单词及其出现次数到result.txt文件中

    fprintf(fpx, "\n");

    for (i = 1; i < size; i++)                //输出统计结果
    {
        printf("%s   %d\n", list[i].word, list[i].time);
        all = all + list[i].time;
    }
    printf("\n");

    printf("总共有 %d 个单词\n", all);    //////输出该文章一共有多少个单词数量

    fprintf(fpx, "总共有 %d 个单词\n", all);

    printf("\n");

    fclose(fpx);
}

void display() //展示统计结果函数

{
    int i, j;

    char x;

    int len = 0; //初始时字符串长度为0

    root = (Word*)malloc(sizeof(Word));

    for (i = 0; i < 26; i++)
        root->next[i] = NULL;

    FILE* fp;
    fp = fopen("C:\\Users\\26217\\Desktop\\file.txt", "r");

    while ((x = fgetc(fp)) != EOF)
    {
        if ((x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z'))
        {
            tempword[len] = x;
            len++;
        }
        else
        {
            tempword[len] = '\0';

            CreateWord(tempword);//////读取一个字符,当其为空格、?等符号时,其前面的字符就组成一个单词。

            len = 0;
        }
    }

    tempword[len] = '\0';

    CreateWord(tempword); //创建对文本最后一个英文字符串的读取

    //////相当于所有单词都已进入树

    len = 0;

    fclose(fp);

    ReadWord(root, 0);

    struct tlist temp;

    //输出单词的总个数
    printf("总的英文单词类型个数为:%d 种\n", size - 1);

    for (i = 0; i < size - 1; i++) //冒泡

        for (j = i + 1; j < size; j++)

            if (strcmp(list[i].word, list[j].word) > 0)

            {

                temp.time = list[i].time;
                list[i].time = list[j].time;
                list[j].time = temp.time;

                strcpy_s(temp.word, list[i].word);
                strcpy_s(list[i].word, list[j].word);
                strcpy_s(list[j].word, temp.word);

            }

    output();
}

int main() //主函数
{
    display();
    return 0;
}

img

一模一样的代码怎么一边有bug一边没bug

我不会,我也想知道原因。我检查了一下,没有发现bug,为什么我们的运行结果不一样?

img

结束标志部分

对比看下这个实例【数据结构 C++实现 基于不同策略的英文单词的词频统计和检索系统】,链接:https://blog.csdn.net/qq_54388490/article/details/124072872

当需要内存过大,会出现有时候对有时候错

这段代码是一个字典树的实现,用于统计文本中单词的出现频率。在分析代码时,我注意到一些问题:

  1. 在定义结构体数组 list 时,分配的空间大小是 3000000,但是在读入文本并统计单词频率时,单词的总数是由变量 all 统计的,并没有限制 all 的最大值。如果单词总数超过 3000000,可能会导致程序崩溃或数据错误。

  2. 在 CreateWord 函数中,在处理大写字母时,将大写字母转换成小写字母的代码可能不能正常工作,因为大写字母和小写字母的 ASCII 码值之差不是 26(即 'A' 和 'a' 的 ASCII 码值之差)。

  3. 在 CreateWord 函数中,字符串 str 的最后一个字符总是被忽略。这是因为遍历字符串的循环条件是 i < len,而不是 i <= len。

  4. 在 CreateWord 函数中,如果字符串 str 中的字符不是字母,则会导致程序崩溃,因为变量 id 会被赋值为 0,导致程序尝试访问空指针。
    在 output 函数中,如果在输出每个单词前面的数字时,如果单词总数超过了 3000000,则可能会导致程序崩溃或数据错误,因为在调用 printf 函数时,使用了不正确的格式字符串。正确的做法应该是使用 %d 而不是 %c 来输出数字。

  5. 在 output 函数中,使用了 strcmp 函数来比较两个字符串的大小。但是,strcmp 函数并不会忽略字符串中的空格、标点符号等非字母字符,因此,如果两个字符串中包含这些字符,则可能会得到不正确的比较结果。

  6. 在 display 函数中,使用了 strcmp 函数来比较两个字符串是否相等。但是,同样的,strcmp 函数并不会忽略字符串中的空格、标点符号等非字母字符。因此,如果两个字符串中包含这些字符,则可能会得到不正确的比较结果。

  7. 在 main 函数中,使用了 clock 函数来测量程序运行时间。但是,clock 函数并不是精确的时间测量方法,因为它并不能准确地反映程序运行时间。
    下面是修复上述 8 个问题的建议:

  8. 对于统计单词频率的结构体数组 list,可以使用动态内存分配来避免内存浪费。可以使用 malloc 函数在运行时为数组分配内存,并使用 realloc 函数在需要时动态调整数组大小。例如:

// 定义结构体数组
struct tlist* list = NULL;

// 分配内存
list = malloc(sizeof(struct tlist) * 3000000);
if (list == NULL) {
    // 内存分配失败,输出错误信息
    printf("Error: malloc failed!\n");
    exit(-1);
}

// 在需要时动态调整数组大小
list = realloc(list, sizeof(struct tlist) * new_size);
if (list == NULL) {
    // 内存分配失败,输出错误信息
    printf("Error: realloc failed!\n");
    exit(-1);
}
  1. 在读入文本时,可以使用更多的预处理技术来去除文本中的非字母字符,例如使用正则表达式等。可以使用 regex.h 库中的函数,例如 regcomp 和 regexec 来实现正则表达式匹配。例如:
#include <regex.h>

int main() {
    // 定义正则表达式
    char* pattern = "[a-zA-Z]+";
    regex_t reg;

    // 编译正则表达式
    int ret = regcomp(&reg, pattern, REG_EXTENDED);
    if (ret != 0) {
        // 正则表达式编译失败,输出错误信息并退出
     char error[100];
    regerror(ret, &reg, error, sizeof(error));
    printf("Error: regcomp failed! %s\n", error);
    exit(-1);
}
// 读入文本
char str[1000];
fgets(str, sizeof(str), stdin);

// 匹配文本
ret = regexec(&reg, str, 0, NULL, 0);
if (ret == 0) {
    // 文本符合正则表达式,输出结果
    printf("Match found!\n");
} else if (ret == REG_NOMATCH) {
    // 文本不符合正则表达式,输出结果
    printf("Match not found!\n");
} else {
    // 匹配失败,输出错误信息
    char error[100];
    regerror(ret, &reg, error, sizeof(error));
    printf("Error: regexec failed! %s\n", error);
    exit(-1);
}

// 释放资源
regfree(&reg);

return 0;
}
  1. 在统计单词频率时,可以使用哈希表或其他数据结构来加快查找速度。例如,可以使用 unordered_map 库中的哈希表来存储单词及其频率。例如:
#include <unordered_map>

int main() {
    // 定义哈希表
    std::unordered_map<std::string, int> word_count;

    // 读入文本
    std::string str;
    std::cin >> str;

    // 统计单词频率
    ++word_count[str];

    // 输出结果
    std::cout << "The frequency of " << str << " is " << word_count[str] << std::endl;

    return 0;
}
  1. 在输出结果时,可以使用更好的输出格式,例如使用表格的形式。例如:
int main() {
    // 读入文本
    char str[1000];
   fgets(str, sizeof(str), stdin);
 // 统计单词频率
std::unordered_map<std::string, int> word_count;
std::stringstream ss(str);
std::string word;
while (ss >> word) {
    ++word_count[word];
}

// 输出结果
std::cout << "Word" << "\t" << "Frequency" << std::endl;
for (const auto& p : word_count) {
    std::cout << p.first << "\t" << p.second << std::endl;
}

return 0;
}