【问题标题】:Linked List Word Frequency and Sort C++链表词频和排序 C++
【发布时间】:2014-03-03 08:31:19
【问题描述】:

我正在编写一个程序,它从文本文件中读取单词并将所有这些单词放入链接列表中。该文件没有标点符号,只有单词。我还想将链表与预加载的黑名单进行比较,黑名单也是链表。

我已经完成的是我可以从文件中加载链接列表,打印链接列表,检查大小,计算一个单词在文件中出现的频率,而不是打印低于指定的单词频率,并且我还能够将所有单词格式化为小写以便更好地处理。

我遇到的问题是让代码正确,以便它只打印一次出现多个频率的单词。因此,如果单词“the”出现 20 次,我不希望它在下次出现时打印“the ”然后打印“the ”,清除“the ”我只是希望它打印一次“”

我正在发布我的加载文件功能、打印功能和插入字功能,这些都是class wordCloud() 的一部分。

下面是代码:

void wordCloud::insertWord(string aWord){
wordNode *newWord = new wordNode(aWord);

//old code
if (head == NULL)
    head = newWord;
else{
    newWord->next = head;
    head = newWord;
}

//revised code
//newWord->next = head;
//head = newWord;
size++;
}

void wordCloud::insertWordDistinct(string word){
for (wordNode *temp = head; temp != NULL; temp = temp->next){
    if (word == temp->myWord){
        temp->freq_count++;
        //cout << temp->freq_count; //for debugging
    }
}
insertWord(word);
}

void wordCloud::printWordCloud(int freq){
wordNode *temp, *previous;
int listSize = 0;

if (head == NULL)                   //determines if there are any words in the list
    cout << "No Word Cloud" << endl;
else{
    temp = head;

    while (temp->next != NULL){         //prints each word until the list is NULL
        if (temp->freq_count >= freq){
            cout << temp->myWord << " <" << temp->freq_count << ">" << endl;
            temp = temp->next;
            listSize++;
        }
        else{
            previous = temp;
            temp = temp->next;
            previous = NULL;
            free(previous);
        }
    }
}
cout << "\nThere are " << size << " words in the file.\n";      //print file size - for debugging - works
cout << "\nThere are " << listSize << " words in the list\n\n";     //print list size - for debugging - works
system("pause");
}

void wordCloud::printBlacklist(){
wordNode *temp;

if (head == NULL)                   //determines if there is a list
    cout << "No Words in the blacklist" << endl;
else{
    temp = head;

    while (temp != NULL){           //prints each word until the list is NULL
        cout << temp->myWord << endl;
        temp = temp->next;
    }
}
cout << "\nThere are " << size << " words in the file.\n\n";        //print size - for debugging - works
system("pause");
}

void wordCloud::loadWordCloud(string fileName){
ifstream file;                      //variable for fileName
string word;                        //string to hold each word

file.open(fileName);                //open file

if (!file) {                        //error handling
    cout << "Error: Can't open the file. File may not exist.\n";
    exit(1);
}

while (!file.eof()){
    file >> word;                   //grab a word from the file one at a time

    insertWordDistinct(changeToLowerCase(word));
    //insertWord(word);             //for debugging
    //cout << word <<'\n';          //print word - for debugging
}

//printWordCloud();                 //print word cloud - for debugging - works
file.close();                       //always make sure to close file after read
}

void wordCloud::loadBlacklist(string fileName){
ifstream file;                      //variable for fileName
string bannedWord;                  //string to hold each word  

file.open(fileName);                //open file

if (!file) {                        //error handling if file does not load
    cout << "Error: Can't open the file. File may not exist.\n";
    exit(1);
}   

while (!file.eof()){
    file >> bannedWord;             //grab a word from the file one at a time

    if (bannedWord.empty()){        //error handling if file is empty
        cout << "File is empty!!\n";
        exit(1);
    }
    insertWord(changeToLowerCase(bannedWord));
    //cout << bannedWord << '\n';   //print blacklist words - for debugging
}

//printBlacklist();                 //print blacklist - for debugging - works
file.close();                       //always make sure to close file after read
}

我注意到,如果我将previous = NULL 放在free() 之前,我的程序不会崩溃,也不会出现任何 dll 内存处理错误。事实上,我可以完全删除free(),它似乎工作得很好。我只是不知道这是否是正确的方法。在我看来,如果我只是将一个节点指向 NULLfree() 或delete() 来终止节点感到不安。如果我错了,请纠正我,或者请直接指出我的权利。

差不多,这有什么问题:

wordNode *previous, *temp = head;

while (temp != NULL){
    if (word == temp->myWord){
        temp->freq_count++;
        previous = temp;
        temp = temp->next;
        delete(previous);
    }
}

我可能做错了,但基本上我只需要找到插入列表中的每个单词的频率,然后删除包含该单词的多个节点,直到只剩下频率计数最高的节点打印。我正在尝试在insertWordDistinct(string word) 中执行此操作以完成此操作。只是不知道该怎么做。

【问题讨论】:

  • 这是一个错误的描述!
  • “我开始使用 free() 是因为 delete() 也给我带来了内存处理问题” - 根本无法描述该标志的 red 程度。如果你没有malloc(),你就不需要free()(你也不需要malloc(),所以都不应该在这段代码中)。您现在不妨说一下这是否适用于学术界,因为这将有助于避免随之而来的“您使用错误的容器”cmets 的暴风雪。
  • 230 LOC 不是 SSCCE。请编辑您的问题,以便更容易回答。
  • 关于单词排序和频率计算,如果您对标准库有充分的信心和信誉,I strongly advise you use it
  • @Johnsyweb - 我发布了我所有的代码,因为老实说,我不知道我的所有错误到底在哪里导致我的程序因内存错误而崩溃。我尝试调试,但最终在我的代码中的不同点处中断,这些点对我来说甚至不知道问题可能是什么。

标签: c++ sorting linked-list word-frequency word-cloud


【解决方案1】:

您的打印循环对您没有任何帮助。它应该是对最小频率的简单枚举过滤。不应进行删除、释放或其他内存管理。只需遍历列表:

void wordCloud::printWordCloud(int freq)
{
    int listSize = 0;
    int uniqSize = 0;
    for (wordNode *temp = head; temp; temp = temp->next)
    {
        if (temp->freq_count >= freq)
        {
            cout << temp->myWord << " <" << temp->freq_count << ">" << endl;
            listSize += temp->freq_count;
            ++uniqSize;
        }
    }

    cout << "\nThere are " << size << " words in the file.\n";
    cout << "\nThere are " << listSize << " words in the filtered list\n\n";
    cout << "\nThere are " << uniqSize << " unique words in the filtered list\n\n";
    system("pause");
}

这也应该让您重新正确管理 wordCloud::~wordCloud() 析构函数中的列表,以再次正确删除节点。还有很多其他的事情我会做不同的事情,但这是一个学习过程,所以我不会破坏你的聚会。


更新

根据 OP 的请求,下面是一个示例链表插入函数,该函数在构建列表时进行插入排序。在调整这一点时,他发现了与原始实现的显着差异和问题。希望它也对其他人有所帮助。

void wordCloud::insert(const std::string& aWord, unsigned int freq)
{
    // manufacture lower-case version of word;
    std::string lcaseWord = make_lower(aWord);

    // search for the word by walking a pointer-to-pointer
    //  through the pointers in the linked list.
    wordNode** pp = &head;
    while (*pp && ((*pp)->myWord < lcaseWord)
        pp = &(*pp)->next;

    if (*pp && !(lcaseWord < (*pp)->myWord))
    {
        (*pp)->freq_count++;
    }
    else
    {    // insert the node
        wordNode *node = new wordNode(lcaseWord);
        node->freq_count = freq;
        node->next = *pp;
        *pp = node;
        ++size;
    }
}

【讨论】:

  • 所以它是我的析构函数,加上我的printWordCloud() 中的错误代码,这给了我你认为的内存问题?好的,我会进一步研究它。我还看到 listSize 现在是数千个,这意味着它只是将所有频率计数相加。当我弄清楚如何删除我猜测的列表中的多余单词时,这应该可以解决。
  • @CharlWillia6 上面代码中的listSize 是字数(包括重复),即它是给定频率约束的总字数。 uniqSize 是结果列表中唯一单词的数量,可能是您要查找的值。
  • listSize 现在给了我成千上万的数字。所以它正在做的是将频率计数相加,所以每次“”然后是“”,直到“”被打印出来,它将所有这些频率计数加在一起。我似乎无法弄清楚如何从列表中只打印一次“the”。实际上,更好的是,我希望从列表中删除所有其他“the”,除了具有最大 freq_count 的最后一个。但是listSize 现在是数千个,而文件中只有 472 个单词。如果listSize 没有打印出所有出现的单词,那么它是正确的。
  • @CharlWillia6 那你的插入代码有问题。也许我错过了你想要做的事情。当您阅读文件时,您是否希望结果链接列表中的每个 单词都包含重复项?或者只是每个单词的一个实例,并附有频率计数?我问是因为insertWord 盲目地将单词放在列表的末尾,而insertDistinctWord 似乎至少在尝试做后者。
  • @CharlWillia6 只是为了阅读不同的内容,you may find this interesting
【解决方案2】:

我认为要打印每个单词一次,您必须创建一个唯一列表,其中包含原始列表中的单词以及它们的出现次数。为此,您只需要两个循环。一个用于从原始列表中获取每个单词,第二个用于检查单词是否在唯一列表中。为此,您应该制作第二个列表并将每个单词复制一次,如果单词出现不止一次,您只需增加频率。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 2020-10-12
    • 1970-01-01
    • 2011-08-07
    • 2012-04-20
    • 1970-01-01
    相关资源
    最近更新 更多