利用list-head 设计完成一个单词计数系统。
一、实现思路
定义一个结构体用于存储单词及单词出现次数,其中结构体成员包括存储单词的char数组ch,计数器num,及list_head。
依次从文件中读入字符,得到合法单词后用list_for_each()遍历判断链表中是否已存在该单词,若没有,则插入,并将计数器赋值为1;若已有,则不插入,只累加计数器值。
此时已经得到每个合法单词及它们的出现次数,再根据计数器num值进行排序,打印输出即为最后结果。分别尝试了冒泡排序和选择排序两种排序算法,其中可能因为冒泡排序内层循环的临界值确定有误,所以冒泡排序还存在些许问题,便换成了选择排序。
二、主要算法
在链表中添加新结点:
void __list_add(struct list_head *add, struct list_head *prev, struct list_head *next){
next->prev = add;
add->next = next;
add->prev = prev;
prev->next = add;
}
判断链表是否为空:
int list_empty(struct list_head *head){
return head->next == head;
}
选择排序:
void list_sortb(struct list_head *head){
int max,t;
char tc[50];
struct list_head *tmp=head->next,*plist;
while(tmp->next != head){
struct list_head *next = tmp->next;
link *node1 = list_entry(tmp,link,list);
max=node1->num;
plist=tmp;
while(next!=head){
link *node2 = list_entry(next,link,list);
if(max<node2->num){
max=node2->num;//更新最大值
plist=next;//plist用于记录此轮循环最大值所在位置
}
next=next->next;
}
if(max!=node1->num){//改变结点内值的内容
link *nodet = list_entry(plist,link,list);
t=node1->num;
node1->num=nodet->num;
nodet->num=t;
strcmp(tc,node1->ch);
strcmp(node1->ch,nodet->ch);
strcmp(nodet->ch,tc);
}
tmp=tmp->next;
}
}
冒泡排序:
void list_sorta(struct list_head *head) {
struct list_head *tmp=head->next,*plist;
struct list_head *q=head->prev;
while(tmp->next != head){
struct list_head *next = tmp->next;
struct list_head *prev = tmp->prev;
while(next!=q){
link *node1 = list_entry(tmp,link,list);
link *node2 = list_entry(next,link,list);
if(node1->num<node2->num){
printf("%d\n",node1->num);
prev->next=next;
next->prev=prev;
tmp->next=next->next;
next->next->prev=tmp;
next->next=tmp;
tmp->prev=next;
}
next=next->next;
q=q->prev;
}
tmp=prev->next->next;
}
}
三、代码
#include "stdio.h"
#include "string.h"
#include <malloc.h>
#include "list.h"
//引入list.h头文件,list_head操作都定义在里面
//虚词数组
char xuCi[17][105]={"a","an","the","in","on","from","above","behind",
"and","but","before","oh","well","hi","hel","of","for"};
// 定义该结构体存储单词及计数器
typedef struct _link{
char ch[50];
int num;
struct list_head list;
}link;
//通过num值进行排序(选择)
void list_sortb(struct list_head *head){
int max,t;
char tc[50];
struct list_head *tmp=head->next,*plist;
while(tmp->next != head){
struct list_head *next = tmp->next;
link *node1 = list_entry(tmp,link,list);
max=node1->num;
plist=tmp;
while(next!=head){
link *node2 = list_entry(next,link,list);
if(max<node2->num){
max=node2->num;//更新最大值
plist=next;//plist用于记录此轮循环最大值所在位置
}
next=next->next;
}
if(max!=node1->num){//改变结点内值的内容
link *nodet = list_entry(plist,link,list);
t=node1->num;
node1->num=nodet->num;
nodet->num=t;
strcmp(tc,node1->ch);
strcmp(node1->ch,nodet->ch);
strcmp(nodet->ch,tc);
}
tmp=tmp->next;
}
}
//通过num值排序(冒泡)
void list_sorta(struct list_head *head) {
struct list_head *tmp=head->next,*plist;
struct list_head *q=head->prev;
while(tmp->next != head){
struct list_head *next = tmp->next;
struct list_head *prev = tmp->prev;
while(next!=q){
link *node1 = list_entry(tmp,link,list);
link *node2 = list_entry(next,link,list);
if(node1->num<node2->num){
printf("%d\n",node1->num);
prev->next=next;
next->prev=prev;
tmp->next=next->next;
next->next->prev=tmp;
next->next=tmp;
tmp->prev=next;
}
next=next->next;
q=q->prev;
}
tmp=prev->next->next;
}
}
int main(){
FILE *fp;
fp = fopen("d:\\test\\data.txt","r");
char word[100],c;
int pos = 0,i;
int flag=0,flag1=0;
struct list_head head,*plist;
link *ptmp;
INIT_LIST_HEAD(&head); //初始化
while (!feof(fp)){
flag1=0;
flag=0;
c = fgetc(fp); //逐个获取字符
if ((c>='a'&&c<='z')||(c>='A'&&c<='Z'||c=='-'||c=='\''))
word[pos++]=(c>='A'&&c<='Z')?c+32:c;//全转成小写,就可以无视大小写统计了
else if (pos>0) {
word[pos] = '\0';
for(i=0;i<17;i++){//判断是否是虚词
if(strcmp(word,xuCi[i])==0)flag=1;
}
// 链表遍历,比较链表中的节点值与当前单词
if(!list_empty(&head)){
list_for_each(plist,&head){
link *node = list_entry(plist,link,list);//取得数据项,list_entry一般和list_for_each配合使用
if (strcmp(word, node->ch)==0){
flag1=1;
node->num++;//不插入,计数器累加
}
}
}
// 如果链表中没有当前单词,在链表末尾插入节点
if (flag1==0&&flag==0){
ptmp = (link *)malloc(sizeof(link));
strcpy(ptmp->ch, word);
ptmp->num=1;
list_add_tail(&(ptmp->list),&head);
}
pos=0;
}
}
fclose(fp); // 对文件进行操作,关闭文件
list_sortb(&head);//调用排序算法
list_for_each(plist,&head)//遍历链表,打印结果
{
link *node = list_entry(plist,link,list);
printf("%-20s %d\n",node->ch,node->num);
}
return 0;
}
list.h头文件内容:
#ifndef _LIST_H
#define _LIST_H
#define _INLINE_ static inline
#define LIST_HEAD_INIT(name) {&(name), &(name)}
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { (ptr)->next = (ptr);(ptr)->prev = (ptr);} while (0)
#define list_entry(ptr, type, member) ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
#define list_for_each(pos, head) for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_safe(pos, pnext, head) for (pos = (head)->next, pnext = pos->next; pos != (head); pos = pnext, pnext = pos->next)
struct list_head {
struct list_head *next, *prev;
};
_INLINE_ void __list_add(struct list_head *add,
struct list_head *prev,
struct list_head *next){
next->prev = add;
add->next = next;
add->prev = prev;
prev->next = add;
}
_INLINE_ void list_add(struct list_head *add, struct list_head *head){//头插法
__list_add(add, head, head->next);
}
_INLINE_ void list_add_tail(struct list_head *add, struct list_head *head){//在表尾添加节点
__list_add(add, head->prev, head);
}
_INLINE_ int list_empty(struct list_head *head){//判断链表是否为空
return head->next == head;
}
#undef _INLINE_
#endif
四、运行结果