利用list-head 设计完成一个单词计数系统

利用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

四、运行结果

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值