一道笔试题:统计文章中出现的单词个数
题目要求:
请设计数据结构并写出算法,统计一篇文章中每个单词出现的次数,并按单词在文章中出现的顺序输出单词及它的个数。
个人思路:
采用了类似键树的思想,用该树来存储单词及某个单词出现的次数;树从根节点到叶子节点组成一个单词,每个节点只存储该单词里的某个字符;
用孩子兄弟法表示树,用左孩子记录某个单词出现的次数(参见树结构图),可让每个单词必有左孩子(创建新节点同时创建左孩子)。
这样就能得到单词及其个数,题目中要求按出现顺序输出可以用一个list或vector存放单词记录,该记录里存放单词及指向代表该单词访问记录数的树节点指针,这样文章读完后,将list或vector里的记录输出即可。
其它思路:
(1)可以用stl里的map实现
(2)可以将单词hash,存放在一个数组里,若hash冲突,采用链地址法,在数组元素后添加链表存放单词及次数,同时将首次出现的记录指针存放到Vector或list中
实现:
定义树结构及单词记录如下:
//孩子兄弟法表示树 typedef struct KeyNode { char data; unsigned int count; struct KeyNode *son,*next; }*KeyTree; //单词记录 //存放单词及指向单词出现数的节点的指针 struct TWord { string word; struct KeyNode* pNode; };
树结构示意图:
上图展示的是查询过单词"ah","she"后形成的树;
以"ah"单词访问为例,描述如何构造树:
(1)初始化一个树,生成一个根节点和一存放空字符的左孩子
(2)单词"ah",先从树中找字符'o',没找到,则在树根插入节点,赋值为'a',同时创建存放空字符的左孩子,次数设为0(这个次数,表示单词"a"出现的次数)
(3)之后按同样方法将字符'h'按(2)中方法生成新节点插入到(2)中生成节点'a'下
(4)单词读完,将'h'的左子树出现次数+1,表示单词"ah"出现次数为1;同时将该左子树指针与单词名放入vector
(5)同样方法生成"she"单词及其次数
若出现同样的单词,只需将单词对应的尾字符的左孩子节点次数+1即可。
查找及插入单词用到的函数:
//从给定键树中查看是否存在字符串pStr, //若存在返回true,若是第一次查询该字符串,则记字符串为首次查询且用p返回 //指向该字符串结束叶子节点的指针; //若不存在返回false bool FindString(const KeyTree pTree,char* pStart,const char* pEnd,bool& isFirstFind,KeyTree& p) { assert(pTree!=NULL && pTree->son!=NULL); assert(pStart!=NULL); assert(pEnd!=NULL); char* pCur = pStart; KeyTree pCurTree = pTree->son; while(pCurTree) { KeyTree pNext = pCurTree->next; //从兄弟中找相应字符直到找到若兄弟为空 while(pNext && pNext->data!=*pCur) { pNext = pNext->next; } //没找到 if(pNext==NULL) return false; //找到进入下一层判断 pCurTree = pNext->son; pCur++; //字符串匹配结束,成功找到 if(pCur>pEnd) break; } KeyTree pSon = pCurTree; //第一次访问,孩子节点不存在,创建 if(pSon == NULL) { pSon = CreateKeyNode(END_WORD); pSon->count = 1; isFirstFind = true; p = pSon; } else { //出现次数+1 pSon->count++; if(pSon->count==1) { isFirstFind = true; p = pSon; } } return true; }
//将字符串插入到键树中 //从第一个不匹配的字符开始创建新树节点存储字符串里的字符 //p返回指向该字符串结束叶子节点的指针 bool Insert(const KeyTree pTree,char* pStr,const char* pEnd,KeyTree& p) { assert(pTree!=NULL && pTree->son!=NULL); assert(pStr!=NULL); assert(pEnd!=NULL); char* pCur = pStr; KeyTree pCurTree = pTree->son; while(pCur<=pEnd) { KeyTree pNext = pCurTree->next; //从兄弟中找对应字符,直至找到或找完所有兄弟 while(pNext && pNext->data!=*pCur) { pCurTree = pNext; pNext = pNext->next; } //未找到,则创建 if(pNext==NULL) { pNext = CreateKeyNode(*pCur); pNext->son = CreateKeyNode(END_WORD); pCurTree->next = pNext; } //当前字符匹配完成,匹配下一个字符 pCurTree = pNext->son; pCur++; } //出现次数+1 pCurTree->count++; p = pCurTree; return true; }
对字符串的输出:
附上源码文件:类键树统计字符串个数.zip