通讯录的简单制作

今年的小学期算法课设抽到了通讯录制作的题目,用了两三天和队友一起完成,整体没有用到什么难以理解的算法,仅仅是用到了快速排序和折半查找。目的是在此记录一下,给需要的同学们一点参考。

目录

基本描述

初步思路

核心代码

记录姓名属性的首字母提取

插入新纪录

链表记录的保存

本地txt文件导入链表

其他模块

结束语


基本描述

(1)算法目标:

   通讯录插入时能够有效率的插入并按姓氏字母顺序排序。

(2)数据输入:

   用户输入指令。

   Enter:向链表中插入一条新的纪录;

   Display:显示一条记录;

   Search:查找一条记录;

   Delete:删除一条记录;

   Save:将当前链表中的记录写入txt文件保存本地;

   Load:将本地txt文件中的记录载入链表;

(3)数据输出:

   Display:输出显示的记录

   Search:输出查找的记录

typedef struct person
{
 string name;
 string street;
 string city;
 string state;
 string EIP; 
 struct person *prior, *next;
} singlePerson;//通讯录个体Node 

typedef struct addressList
{
 singlePerson *head;
 singlePerson *end;
 int length;
} addressList;//通讯录双向链表 

初步思路

       数据结构采用双向链表的结构来储存用户输入的个体信息,插入时采取按照输入信息中的名字首字母顺序进行排序,插入时查找插入位置采取分治法中的二分法进行查找;载入文件时先将文件中的记录导入链表,再采用快速排序的方法进行链表的排序。

核心代码

记录姓名属性的首字母提取

void get_first_letters(string szChinese,char piyinBuf[],const unsigned int maxBufLen,unsigned int &piyinBufLen)
{
	unsigned char chr[3];
	piyinBufLen = 0;
	unsigned int chineseLen = szChinese.size();
	for (unsigned int i = 0; i < chineseLen && piyinBufLen < maxBufLen; ++i)
	{
		unsigned char c = szChinese[i];
		// 排除askii 码
		if (isascii(c))
		{
			piyinBuf[piyinBufLen++] = c;
		}
		else
		{
			chr[0] = szChinese[i];
			chr[1] = szChinese[++i];
			chr[2] = 0;
			wchar_t wchr = 0;
			wchr =  (chr[0]  &  0xff)  <<  8;
			wchr |= (chr[1]  &  0xff);
			piyinBuf[piyinBufLen++] = toupper(get_first_letter(wchr));
		}
	}
}

bool between(wchar_t start,wchar_t end,wchar_t aim)
{
	if (start <= aim && aim <= end)
	{
		return true;
	}
	return false;
}

char get_first_letter(wchar_t wchar)
{
	if  (between(0xB0A1,0xB0C4,wchar))  return  'a';
	if  (between(0XB0C5,0XB2C0,wchar))  return  'b';
	if  (between(0xB2C1,0xB4ED,wchar))  return  'c';
	if  (between(0xB4EE,0xB6E9,wchar))  return  'd';
	if  (between(0xB6EA,0xB7A1,wchar))  return  'e';
	if  (between(0xB7A2,0xB8c0,wchar))  return  'f';
	if  (between(0xB8C1,0xB9FD,wchar))  return  'g';
	if  (between(0xB9FE,0xBBF6,wchar))  return  'h';
	if  (between(0xBBF7,0xBFA5,wchar))  return  'j';
	if  (between(0xBFA6,0xC0AB,wchar))  return  'k';
	if  (between(0xC0AC,0xC2E7,wchar))  return  'l';
	if  (between(0xC2E8,0xC4C2,wchar))  return  'm';
	if  (between(0xC4C3,0xC5B5,wchar))  return  'n';
	if  (between(0xC5B6,0xC5BD,wchar))  return  'o';
	if  (between(0xC5BE,0xC6D9,wchar))  return  'p';
	if  (between(0xC6DA,0xC8BA,wchar))  return  'q';
	if  (between(0xC8BB,0xC8F5,wchar))  return  'r';
	if  (between(0xC8F6,0xCBF0,wchar))  return  's';
	if  (between(0xCBFA,0xCDD9,wchar))  return  't';
	if  (between(0xCDDA,0xCEF3,wchar))  return  'w';
	if  (between(0xCEF4,0xD188,wchar))  return  'x';
	if  (between(0xD1B9,0xD4D0,wchar))  return  'y';
	if  (between(0xD4D1,0xD7F9,wchar))  return  'z';
	return  '\0';
}

此处参考了https://blog.csdn.net/wangkr111/article/details/7952085的方法并对输入的类型进行了稍微的调整。具体的原理各位可以移步链接去研究一下关于汉字的区位码的计算。

但是注意!这个方法仅仅适用于一般的常用汉字!这个方法对于稍微生僻一点的字无效!

插入新纪录

插入新纪录运用的方法是按照姓名首字母的顺序来进行插入位置的查找并进行双向链表的插入。

int find_enter_pos(addressList &record,singlePerson *elem){
	int low=0,high=record.length-1;
	int mid;
	char* k=get_pinyin(elem);
	singlePerson *Mid_elem;
	char *r;
	while(low<=high){
		Mid_elem=record.head;
		mid=(low+high)/2;
		for(int i=0;i<mid;i++){
			Mid_elem=Mid_elem->next;
		}
		r=get_pinyin(Mid_elem);
		if(strcmp(k,r)<0) {high=mid-1;}
		else if(strcmp(k,r)>0) {low=mid+1;}
		
	}
	return low;
}
char * get_pinyin(singlePerson *elem){
	char *buf=new char[1024];
	for(int i=0;i<1024;i++){
		buf[i]='\0';
	}
    unsigned int  len = 0;
	string a=elem->name;
	get_first_letters(a,buf,1024,len);
	return buf;
}

寻找插入位置时运用了折半查找,针对具体的需求对传统的折半查找做了相应的修改。找到插入位置后进行双向链表新纪录的插入即可,这里不再详述。

链表记录的保存

void save(const addressList &record)
{
 	singlePerson *elem=record.head;
	string cache;
	cin>>cache;//文件路径输入 
	const char *p = cache.c_str();
	ofstream OutFile(p);
	if(!OutFile) cout<<"Error";
	else{
		string str="	";
		while(elem!=NULL){
			string temp=elem->name+str+elem->street+str+elem->city+str+elem->EIP+str+elem->state;
		 	OutFile << temp;
		 	OutFile <<endl;
		 	elem=elem->next;
		}
		cout<<"保存成功,路径:"<<cache<<endl; 
		OutFile.close();
	}
}

本地txt文件导入链表

void load(addressList &record)
{
 	singlePerson *elem;
	string cache;
	cin>>cache;//文件路径输入 
	const char *p = cache.c_str();
	ifstream fin(p);  
    string s;  
    string str="	";
    vector<vector<string> > info;
    vector<string> _info;
    if(!fin) cout<<"No such File!"<<endl;
    else{
   		while( getline(fin,s) ) 
	    {    
			int pos=s.find(str);
	        while(pos!=s.npos){
	        	_info.push_back(s.substr(0,pos));
	        	s=s.substr(pos+1,s.size());
				pos=s.find(str);
	        }
	         _info.push_back(s);
			info.push_back(_info);
			_info.clear();	
	    } 
	    for(int i=0;i<info.size();i++){
    		elem=new singlePerson;
	    	elem->name=info[i][0];
	    	elem->street=info[i][1];
	    	elem->city=info[i][2];
	    	elem->EIP=info[i][3];
	    	elem->state=info[i][4];
	    
	     	if(record.head==NULL){
	 			record.head=elem;record.head->next=elem;record.head->prior=elem;record.end=record.head;
			 } 
 			else{
	 			record.end->next=elem;elem->prior=record.end;record.end=elem;elem->next=NULL;
	 		}
	 		record.length++;
	   		
		}
		
		elem=record.head;
	
		QuickSort(record,0,record.length-1);	
		cout<<"载入成功!"<<endl; 
    }
    
	fin.close();
}

这里将一个Tab键作为分隔符,将文件中的每行记录进行分割,并保存至vector容器中(为了方便插入和清空),最后一并插入链表中,最后对链表进行快速排序。

vector<char*> get_rbuf(const addressList &record)
{
	singlePerson *elem=record.head;
	vector<char* > rbuf;
	int t=0;
	elem=record.head;
	
	while(elem!=NULL)
	{
		char *buf=new char[1024];
		for(int i=0;i<1024;i++){
		buf[i]='\0';
		}
	    unsigned int len = 0;
		string szChinese = elem->name;
		get_first_letters(szChinese,buf,1024,len);
		strlwr(buf);
		rbuf.push_back(buf); 
		t++;
		elem=elem->next;	
	}
	return rbuf;
}
void exchange(singlePerson *a,singlePerson *b){
	singlePerson *elem=new singlePerson;
	elem->name=a->name;
	elem->street=a->street;
	elem->city=a->city;
	elem->state=a->state;
	elem->EIP=a->EIP;
	
	a->name=b->name;
	a->street=b->street;
	a->city=b->city;
	a->state=b->state;
	a->EIP=b->EIP;
	
	b->name=elem->name;
	b->street=elem->street;
	b->city=elem->city;
	b->state=elem->state;
	b->EIP=elem->EIP;
}
int Partition(addressList &record,vector<char *> buf,int first,int end){
	int i=first,j=end;
	int m;
	singlePerson *head=record.head;
	for(m=0;m<first;m++){
		head=head->next;
	}
	singlePerson *tail=record.head;
	for(m=0;m<end;m++){
		tail=tail->next;
	}
	singlePerson *elem;
	while(i<j){
		while(i<j&&strcmp(buf[i],buf[j])<=0){
			j--;
			tail=tail->prior;
		}
		if(i<j){
			exchange(head,tail);
			buf=get_rbuf(record);
			head=head->next;
			i++;
		}
		while(i<j&&strcmp(buf[i],buf[j])<=0){
			i++;
			head=head->next;
		}
		if(i<j){
			exchange(head,tail);
			buf=get_rbuf(record);
			tail=tail->prior;
			j--;
		}
	}
	return i;
}
void QuickSort(addressList &record,int first,int end){
	int pivot;
	if(first<end){
		pivot=Partition(record,get_rbuf(record),first,end);
		QuickSort(record,first,pivot-1);
		QuickSort(record,pivot+1,end);
	}
}

参考传统的快速排序,这里get_rbuf函数返回的是当前链表所有记录姓名属性的首字母数组;partiton函数以get_rbuf返回的数组为依据对链表进行划分,这里注意拼音数组的下标和链表的对应关系;最后进行迭代即可。

其他模块

因为其他模块,像查找、删除等都是双向链表的基本操作,在这里就不进行详述了。

结束语

因为能力有限,做出来的东西瑕疵肯定很多,但是还是要本着分享的精神将我们觉得有意义、值得大家讨论的东西贴出来供大家参考。如有什么有问题的地方可以尽情指出。

原创文章 5 获赞 7 访问量 1726

猜你喜欢

转载自blog.csdn.net/Boooooil/article/details/97965461