2012-01-04 101 views
1

给定一个由单个空格分隔的单词组成的字符串,按照它们出现在字符串中的次数排序,以降序打印单词。C语言频率统计(非C++)

例如“AB BC BC”的输入串将产生以下输出:

bc : 2 
ab : 1 

这个问题将如果C++的数据结构,如地图,使用可以容易地得到解决。但是,如果问题只能在普通的老C中解决,那看起来就更难了。

我应该在这里使用什么样的数据结构和算法?请尽可能详细。我在DS和Algo方面很弱。 :-(

+0

您可以使用链接的结构列表。 – CanSpice 2012-01-04 17:12:09

+0

没有时间进入细节,看看'trie's。 – 2012-01-04 17:12:51

回答

1

下面是如何我做一个样本。 findWord()中的搜索可以被优化。分配数量也可以通过分配文字块而不是一次一个来减少。也可以为这种情况实现一个链表。它缺少内存释放。这应该有希望让你去。

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

    #define MAXWORDLEN 128 

    const char* findWhitespace(const char* text) 
    { 
     while (*text && !isspace(*text)) 
      text++; 
     return text; 
    } 

    const char* findNonWhitespace(const char* text) 
    { 
     while (*text && isspace(*text)) 
      text++; 
     return text; 
    } 

    typedef struct tagWord 
    { 
     char word[MAXWORDLEN + 1]; 
     int count; 
    } Word; 

    typedef struct tagWordList 
    { 
     Word* words; 
     int count; 
    } WordList; 

    WordList* createWordList(unsigned int count); 

    void extendWordList(WordList* wordList, const int count) 
    { 
     Word* newWords = (Word*)malloc(sizeof(Word) * (wordList->count + count)); 
     if (wordList->words != NULL) { 
      memcpy(newWords, wordList->words, sizeof(Word)* wordList->count); 
      free(wordList->words); 
     } 
     for (int i = wordList->count; i < wordList->count + count; i++) { 
      newWords[i].word[0] = '\0'; 
      newWords[i].count = 0; 
     } 
     wordList->words = newWords; 
     wordList->count += count; 
    } 

    void addWord(WordList* wordList, const char* word) 
    { 
     assert(strlen(word) <= MAXWORDLEN); 
     extendWordList(wordList, 1); 
     Word* wordNode = &wordList->words[wordList->count - 1]; 
     strcpy(wordNode->word, word); 
     wordNode->count++; 
    } 

    Word* findWord(WordList* wordList, const char* word) 
    { 
     for(int i = 0; i < wordList->count; i++) { 
      if (stricmp(word, wordList->words[i].word) == 0) { 
       return &wordList->words[i]; 
      } 
     } 
     return NULL; 
    } 

    void updateWordList(WordList* wordList, const char* word) 
    { 
     Word* foundWord = findWord(wordList, word); 
     if (foundWord == NULL) { 
      addWord(wordList, word); 
     } else { 
      foundWord->count++; 
     } 
    } 

    WordList* createWordList(unsigned int count) 
    { 
     WordList* wordList = (WordList*)malloc(sizeof(WordList)); 
     if (count > 0) { 
      wordList->words = (Word*)malloc(sizeof(Word) * count); 
      for(unsigned int i = 0; i < count; i++) { 
       wordList->words[i].count = 0; 
       wordList->words[i].word[0] = '\0'; 
      } 
     } 
     else { 
      wordList->words = NULL; 
     } 
     wordList->count = count;  
     return wordList; 
    } 

    void printWords(WordList* wordList) 
    { 
     for (int i = 0; i < wordList->count; i++) { 
      printf("%s: %d\n", wordList->words[i].word, wordList->words[i].count); 
     } 
    } 

    int compareWord(const void* vword1, const void* vword2) 
    { 
     Word* word1 = (Word*)vword1; 
     Word* word2 = (Word*)vword2; 
     return strcmp(word1->word, word2->word); 
    } 

    void sortWordList(WordList* wordList) 
    { 
     qsort(wordList->words, wordList->count, sizeof(Word), compareWord); 
    } 

    void countWords(const char* text) 
    { 
     WordList *wordList = createWordList(0); 
     Word  *foundWord = NULL; 
     const char *beg = findNonWhitespace(text); 
     const char *end; 
     char  word[MAXWORDLEN]; 

     while (beg && *beg) { 
      end = findWhitespace(beg); 
      if (*end) { 
       assert(end - beg <= MAXWORDLEN); 
       strncpy(word, beg, end - beg); 
       word[end - beg] = '\0'; 
       updateWordList(wordList, word); 
       beg = findNonWhitespace(end); 
      } 
      else { 
       beg = NULL; 
      } 
     } 

     sortWordList(wordList); 
     printWords(wordList); 
    } 

int main(int argc, char* argv[]) 
{ 
    char* text = "abc 123 abc 456 def 789 \tyup this \r\ncan work yup 456 it can"; 
    countWords(text); 
} 
+0

谢谢杰克!我会在免费时仔细查看示例代码。 – 2012-01-04 20:38:00

+0

要在文本中查找单词,可以使用现有的strtok。由于您可能不想修改原始文本(并且strtok会将分隔符更改为空终止符),因此可以先将整个缓冲区复制到一个专用缓冲区中。虽然在POSIX上有一个strtok_r,但代码当然不会重入(线程安全)。或者,只需复制整个缓冲区,将副本中的空白转为空终止符,同时将该词的起始位置保留在不同的指针中。 – CashCow 2012-01-05 09:33:33

+0

@CashCow:非常感谢您对细腻部分的详细解释。 – 2012-01-05 16:04:31

4

,你可以使用一个数据结构,其中包含您可以比较使用的strcmp(我会忽略大小写问题现在)。也就是说一个简单的二进制树

您需要确保树保持平衡你可以看到AVL树或1-2棵树或维基百科或其他地方的红黑树

我不会给出太多的细节,除了要创建一个二叉树结构,每个结点都会有一个可能为空的左右子节点,对于一个叶子节点,两个子节点都是空的。为了使它更简单,使用具有该值和两个子节点的“侵入式”节点,如:

struct Node 
{ 
    char * value; 
    size_t frequency; 
    struct Node * left; 
    struct Node * right; 
}; 

和显然是C你需要做所有的内存管理。

您将有一个函数向下递归树,根据需要进行比较和向左或向右移动。如果发现它会提高频率。如果不是,你的函数应该能够确定插入节点的位置,然后插入和重新平衡逻辑。当然,新节点将包含频率为1的字。

最后,您将需要一种方法来通过树来打印结果。在你的情况下,这可以是一个递归函数。

请注意,替代数据结构会是某种散列表。

如果您正在寻找最高效的解决方案并且手头有大量内存,那么您将使用一种数据结构,从而在您遇到它时分行浏览每个字母。所以“a”给你所有以a开头的单词,然后转到第二个字母“b”等等。对于不知道数据结构的人来说实现起来相当复杂,所以我建议你去用简单的二叉树。

请注意,在打印时,它不会与频率相反,因此您必须先对结果进行排序。 (在使用C++的地图中,你也不会按顺序得到它们)。

+0

谢谢CashCow。这个想法很清楚。我会尽力实现它,这对我来说有点困难(从来没有认真接触过树型东西)。顺便说一句,任何建议,以便在完全建立之后整理出三个(以便按照freq的顺序打印文字)? – 2012-01-04 17:46:15

+0

@Qiang Xu:在我的回复中查看我的示例,在其中您将看到如何使用qsort()。 – Jack 2012-01-04 19:38:06

+0

要对结果进行排序,只需使用qsort,这是C标准的一部分。 – CashCow 2012-01-05 14:53:44

2

我会为此使用三元树。其中数据结构由乔恩·本特利和罗伯特·塞奇威克介绍下面的文章有一个例子C.

http://www.cs.princeton.edu/~rs/strings/

+0

想知道Jon Bentley的“Programming Peals”第二版是否涵盖了这个主题?听说这是一个经典,但没有机会把它放在手上。 – 2012-01-04 20:39:42

+0

我有它在家 - 我会看看我是否可以记得今晚查找它。 – 2012-01-04 21:40:33