圖書管理系統


1.思維導圖

2.數據結構

2.1結構體

typedef struct BOOK
{
	string ISBN,book,author;
	double price;
}BOOK;
typedef struct node
{
	BOOK message;
	struct node *next;
}List,*Link;

2.2為什么選擇這樣的數據結構

這樣的數據結構使程序邏輯條理顯得更加清晰,兩個結構體分工明確
一開始只定義了一個 BOOK的結構體,結構體成員除了書籍信息還包括BOOK型的結構指針,這樣使原來的BOOK結構體擁有兩個結構體的作用,結構體功能不夠明確

3.關鍵代碼

3.1主要函數聲明

bool insert(Link &L,Link newbook);//增加書籍 
void insert_menu();//重載增加書籍函數 
bool del_book(Link &L,string Isbn);//刪除書籍 
void del_book_menu();//刪除書籍菜單 
void findmenu();//查找目錄 
void find(Link L,string search,int chose);//查找書籍 
Link find(Link &L,string name);//重載的查找函數,用於定位需修改的書籍 
void findshow(int n,Link p);//顯示查找到的書籍信息 
void findshow(string search);//提示查找的書籍不存在 
void changebook();//修改書籍信息函數 
void put_file(Link L);//將書籍信息輸出到文件 
void in_file(Link &L);//將書籍信息從文件讀取到鏈表 

3.2 主要功能函數

生成/保存書籍鏈表

void put_file(Link L)//將書籍信息輸出到文件 
{
	ofstream outfile("books.txt");
	Link p,tail=L,r;
	for(p=L->next;p;)
	{
		r=p->next;
		outfile<<p->message.ISBN<<" "<<p->message.book<<" "<<p->message.author<<" "<<p->message.price<<endl;
		p=r;
	}
	
	outfile.close();
}
void in_file(Link &L)//將書籍信息從文件讀取到鏈表 
{
	ifstream infile("books.txt");
	L=new List;
	L->next=NULL;
	Link p,tail=L;
	p=new List;
	p->next=NULL;
	while((infile>>p->message.ISBN>>p->message.book>>p->message.author>>p->message.price)>0)
	{		//這么寫可以防止文件為空時發生錯誤 (這里貌似並沒有什么卵用)
		
		tail->next=p;
		tail=p;
		p=new List;
		p->next=NULL;
	}
	infile.close();
}

查找類函數

void findmenu()//查找目錄 
{
	int  chose=1;
	while(chose==1)
	{
	system("cls");
	printf("請選擇查找類型:\n");
	printf("1:按ISBN碼查找\n");
	printf("2:按書名查找\n");
	printf("3:按作者查找\n");
	
	char search[60];
	while(1)
	{
		int chose_1;
		cin>>chose_1;
		switch(chose_1)
		{
			case 1:cout<<"輸入查找的ISBN碼:"<<endl;break;
			case 2:cout<<"輸入查找的書名:"<<endl;break;
			case 3:cout<<"輸入查找的作者:"<<endl; break;
			default:cout<<"操作錯誤,請重新輸入:"<<endl;break;
		}
		if(0<chose_1&&chose_1<4) break;
	}
	cin>>search; 
	find(L_book,search,chose);
	printf("\n\n輸入 1 繼續查找,其他任意數字返回上一級\n");
	cin>>chose;
	system("cls");
	}
	 
}
void find(Link L,string search,int chose)//查找書籍 
{
	Link p,tail=L;
	int cnt=0;
	for(p=L->next;p;p=p->next)
	{
		if(chose==1&&p->message.ISBN==search)//ISBN 碼查找 
		{
			cnt++;
			findshow(cnt,p);
			break;
		}
		if(chose==2&&p->message.book==search) //書名查找 
		{
			cnt++;
			findshow(cnt,p);
			break;
		}
		if(chose==3&&p->message.author==search)//作者查找 
		{
			cnt++;
			findshow(cnt,p);
		}
	}
	if(cnt==0) findshow(search);
}

void findshow(int n,Link p)
{
	cout<<n<<" "<<p->message.ISBN<<" "<<p->message.book<<" "<<p->message.author<<" "<<p->message.price<<endl;
}
void findshow(string search)
{
	cout<<search<<" 不存在"<<endl;
}
Link find(Link &L,string name)//重載的查找函數,用於定位需修改的書籍 
{
	Link p,tail=L;
	
	for(p=L->next;p;p=p->next)
	{
		if(name==p->message.book)
		return p;
		tail=p;
	}
	return NULL;
}

一開始字符串用的是字符數組,在查找時,只能調用strcmp()函數,較為不方便,后來改成string字符串,這樣就能直接用"==,>,<"這些比較字符進行比較了,而且,string 字符串還可以直接用"+"在字符串尾部追加字符串和字符,都十分方便,除此之外string字符串還有很方便的操作,具體的C++里的STL里都有相關描述。


插入函數

bool insert(Link &L,Link newbook)//插入書籍 ,按價格升序插入 
{
	Link p,tail=L;
	p=L->next;
	for(p=L->next;p;p=p->next)
	{
		if(p->message.ISBN==newbook->message.ISBN)// 以ISBN碼判斷書籍是否已存在 
		{
			 return false;
			 break;
		}
		if(p->message.price>=newbook->message.price)//插入書籍 
		{
			newbook->next=p;
			tail->next=newbook;
			break;
		}
		tail=p;
	}
	if(p==NULL)  //尾部插入 
	tail->next=newbook;
	return true;
}

void insert_menu()//插入書籍,輸入增加的書目信息 
{
	printf("請輸入增加的書籍書目:");//可批量增加書籍 
	int n;
	cin>>n;
	Link p; 
	printf("請輸入待增加書籍的所有信息:");
	while(n--)
	{
		p=new List;
		p->next=NULL;
		cin>>p->message.ISBN>>p->message.book>>p->message.author>>p->message.price;
		if(insert(L_book,p)==0)
		cout<<"Error insert, "<<p->message.book<<" exited!"<<endl;
		else
		cout<<p->message.book<<" 已存入"<<endl; 
	}
	Sleep(2000);
}

這里講一下插入的思路吧,就是遍歷鏈表,比較新書的價格,然后把要插入的書籍插入在價格比它高的第一本書前面,但是這樣的話,如果遍歷完鏈表,沒有找到價格新插入的書籍價格高的書,插入就失敗了,所以后面加了個判斷指針是否為空的語句來避免這種情況。(本來想調用STL 里的llist()和sort(),但list()雙向鏈表排序貌似時間復雜度還蠻高的,所以直接有序插入了)

關於stl_list的sort算法
原文地址
這邊的話一般sort()使用的是快排,把需要排序的序列一分為二,然后各自遞歸調用mergesort,再使用Merge算法用O(n)的時間將已排完序的兩個子序列歸並,從而總時間效率為nlg(n)。list_sort所使用的mergesort形式上大不一樣:將前兩個元素歸並,再將后兩個元素歸並,歸並這兩個小子序列成為4個元素的有序子序列;重復這一過程,得到8個元素的有序子序列,16個的,32個的。。。,直到全部處理完。主要調用了swap和merge函數,而這些又依賴於內部實現的transfer函數(其時間代價為O(1))。該mergesort算法時間代價亦為nlg(n),計算起來比較復雜。list_sort中預留了64個temp_list,所以最多可以處理2^64-1個元素的序列,這應該足夠了:)

刪除函數

void del_book_menu()//刪除書籍界面
{
	system("cls");
	printf("請輸入刪除的書籍數量:");//可批量刪除書籍 
	int n;
	cin>>n;
	cout<<"請輸入所有待刪除書籍的ISBN碼:";
	string Isbn;
	while(n--)
	{
		cin>>Isbn;
		 if(del_book(L_book,Isbn))
		 cout<<"IBSN碼為 "<<Isbn<<" 的書籍已刪除"<<endl;
		 else 
		 cout<<"IBSN碼為 "<<Isbn<<" 的書籍不存在"<<endl;
	} 
	Sleep(2000);
}

bool del_book(Link &L,string Isbn)//刪除書籍 
{
	Link p,tail=L;
	for(p=L->next;p;p=p->next)
	{
		if(p->message.ISBN==Isbn)
		{
			tail->next=p->next;
			delete p;
			break;
		}
		tail=p;
	}
	if(p==NULL)  //刪除書籍不存在 
	return false;
	else return true;
}

修改函數

void changebook()//修改書籍 
{
	int  chose=1;
	Link p;
	while(chose==1) //循環操作 
	{
	system("cls");
	char name[60];
	cout<<"輸入要修改書籍的書名"<<endl;
	cin>>name; 
	p=find(L_book,name);
	if(p=NULL)
	cout<<"該書不存在"<<endl;
	else
	{
		printf("%s %s %s %.2f\n",p->message.ISBN,p->message.book,p->message.author,p->message.price);
		printf("請選擇需要修改的信息:\n");
		printf("1:修改ISBN碼\n");
		printf("2:修改書名\n");
		printf("3:修改作者\n");
		printf("4:修改價格\n");
		printf("5:修改該書所有信息(ISBN 書名 作者 價格)\n");
		while(1)
		{
			int chose_1;
			cin>>chose_1;
			switch(chose_1) //按選擇修改書籍指定信息 
			{
				case 1:
				cout<<"輸入修改后的ISBN碼:"<<endl;
				cin>>p->message.ISBN;break;
				case 2:
				cout<<"輸入修改后的書名:"<<endl;
				cin>>p->message.book;break;
				case 3:
				cout<<"輸入修改后的作者:"<<endl;
				cin>>p->message.author;break;
				case 4:
				cout<<"輸入修改后的價格:"<<endl;
				cin>>p->message.price;break;
				case 5:cout<<"輸入修改后的所有該書信息(ISBN 書名 作者 價格)"<<endl;
				cin>>p->message.ISBN>>p->message.book>>p->message.author>>p->message.price;break;
				default:cout<<"操作錯誤,請重新輸入:"<<endl;break;
			}
			if(0<chose_1&&chose_1<=5) break;
		}
		cout<<"修改成功!"<<endl;
		}
	printf("\n\n輸入 1 修改其他書籍,其他任意數字返回上一級\n");
	cin>>chose;
	}
}

4.待提高的地方

4.1 查找的算法不夠優化。

采用單鏈表遍歷的方式查找書籍,只能將鏈表遍歷一遍,逐個比較查找到所搜索的書籍,如果書目數量太大,查找速度就會很慢;思考了下老師所說的字典序查找,可將書目信息按拼音字母進行分級分類,查找時根據分級的信息科較快的定位到所查找的書籍。這樣查找速度應該會快很多

4.2 文件操作過於頻繁。

起初的代碼只有兩次文件操作,程序開始運行時執行一次讀取文件的操作,和結束程序保存數據到文件的操作,但這樣如果非正常關閉程序,會使之前的操作數據全部丟失,因此改為每次循環保存一次,但這樣的話,如果書籍信息太多,卻會大大降低運行速度

在修改過程中,老師一直強調要減少函數之間的依賴性,可以把函數分成 菜單函數和業務函數 ,菜單函數 主要負責輸入和輸出,也就是運行程序時,我們所能看到的部分,而 業務函數 則負責內部操作,實現的功能,並不能直觀的看到,是一個純粹的功能函數
下面以 查找函數 為例:

一開始的代碼

void  find(Link L,char *search,int chose)//查找書籍 
{
	Link p,tail=L;
	int cnt=0;
	for(p=L->next;p;p=p->next)
	{
		if(chose==1&&strcmp(p->message.ISBN,search)==0)
		{
			cnt++;
			printf("%d %s %s %s %.2f\n",cnt,p->message.ISBN,p->message.book,p->message.author,p->message.price);
			break;
		}
		if(chose==2&&strcmp(p->message.book,search)==0)
		{
			cnt++;
			printf("%d %s %s %s %.2f\n",cnt,p->message.ISBN,p->message.book,p->message.author,p->message.price);
			break;
		}
		if(chose==3&&strcmp(p->message.author,search)==0)
		{
			cnt++;
			printf("%d %s %s %s %.2f\n",cnt,p->message.ISBN,p->message.book,p->message.author,p->message.price);
		}
	}
	if(cnt==0)
	printf("該書不存在\n");
}

這里就把查找和輸出完全糅合在一起,函數能使用的范圍有很大程度的限制,而業務函數因為功能純粹能減小這樣限制。


下面附上主函數

int main()
{
	in_file(L_book);
	int chosen;
	while(1)
	{
		system("cls");
		printf("請選擇操作:\n");
		printf("1:查閱書籍\n");
		printf("2:增加書籍\n");
		printf("3:刪除書籍\n");
		printf("4:修改書籍\n");
		cin>>chosen;
 
		switch(chosen)
		{
			case 1: findmenu();break;
			case 2: insert_menu();break;
			case 3: del_book_menu();break;
			case 4: changebook();break;
			default: printf("輸入錯誤,請重新輸入\n");Sleep(1200);system("cls");
			break;
		} 
		put_file(L_book);
	}
	
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM