图书管理系统(详细版免费)

目录

1、简单操作

2、按价格升序排序图书

3、按价格区间查找图书

4、根据指定书名,进行图书价格的修改

5、图书价格普调

6、查找最贵图书

7、 图书去重

8、完整代码


●此为数据结构与算法实现的练习。

该图书管理系统有以下十个功能:(链表)

0.退出

1. 图书的创建和输出

2. 新书入库(插入)

3. 旧书出库(删除)

4. 按书号查找图书

5. 按价格升序排序图书

6. 按价格区间查找图书

7. 根据指定书名,进行图书价格的修改

8. 图书价格普调

9. 查找最贵图书

10. 图书去重

【注】:功能 8.图书价格普调的含义为:计算所有图书的平均价格,将所有低于平均价格的图书价格提高20%,所有高于或等于平均价格的图书价格提高10%,最后逐行输出价格修改后的全部图书信息。

1、简单操作

对于单链表而言,以下几个操作是基本且常见的:

● 构造一个空的单链表

● 使用头插法或者尾插法创建单链表

● 元素的查找(查找第 i 个元素 / 查找值为 e 的元素  )

● 单链表的插入

● 单链表的删除

以上的操作即满足了图书管理系统里的功能1、2、3、4。

1、构造一个空的单链表:

Status InitList_L(LinkList &L) { 
//构造一个空的单链表L
	L = new LNode; //生成新结点作为头结点,用头指针L指向头结点
	L->next = NULL; //头结点的指针域置空
	return OK;
}

2、使用头插法或者尾插法创建单链表

(此处是由文件读出,也看可以是键盘输入)

头插法:

头插法是通过将新结点逐个插入链表的头部(头结点之后)来创建链表,每次申请一个新结点,读入相应的数据元素值,然后将新结点插入到头结点之后。

【算法步骤】

1、创建一个只有头结点的空链表

2、根据待创建链表包括的元素个数n,循环n次执行一下操作:

● 生成一个新结点 *p

● 输入元素值赋给新结点 *p的数据域

● 将新结点 *p插入到头结点之后

下图为线性表(a,b,c,d,e)头插法的创建过程,因为每次插入在链表的头部,所以应该逆位序输入数据,依次输入e,d,c,b,a,输入顺序和线性表中的逻辑顺序是相反的。

尾插法:

尾插法是通过将新结点逐个插入链表的尾部来创建链表。同头插法一样,每次申请一个新结点,读入相应的数据元素值。不同的是,为了使新结点能够插入表尾,需要增加一个尾指针 r指向链表的尾结点。

【算法步骤】

1、创建一个只有头结点的空链表

2、尾指针 r初始化,指向头结点。

3、根据创建链表包括的元素个数 n,循环n次执行以下操作:

● 生成一个新结点 *p

● 输入元素值赋给新结点 *p的数据域

● 将新结点 *p插入到尾结点*r 之后

● 尾指针r 指向新的尾结点 *p

下图是线性表(a,b,c,d,e)尾插法的创建过程,读入数据的顺序和线性表中的逻辑顺序是相同的。

void CreateList_H(LinkList &L, int n) {  // 头插法创建单链表
//逆位序输入n个元素的值,建立到头结点的单链表L
	LinkList p;
	L = new LNode;
	L->next = NULL; //先建立一个带头结点的空链表
	length = 0;
	fstream file;
	file.open("Book.txt");
	if (!file) {
		cout << "未找到相关文件,无法打开!" << endl;
		exit(ERROR);
	}
	file >> head_1 >> head_2 >> head_3;
	while (!file.eof()) {
		p = new LNode; //生成新结点*p
		file >> p->data.no >> p->data.name >> p->data.price; //输入元素值赋给新结点*p的数据域
		p->next = L->next;
		L->next = p; //将新结点*p插入到头结点之后
		length++;//同时对链表长度进行统计
	}
	file.close();
} 




void CreateList_R(LinkList &L, int n) { // 尾插法创建单链表
//正位序输入n个元素的值,建立带表头结点的单链表L 
	LinkList p, r;
	L = new LNode;
	L->next = NULL; //先建立一个带头结点的空链表
	r = L; //尾指针r指向头结点
	length = 0;
	fstream file; //打开文件进行读写操作
	file.open("Book.txt");
	if (!file) {
		cout << "未找到相关文件,无法打开!" << endl;
		exit(ERROR);
	}
	file >> head_1 >> head_2 >> head_3;
	while (!file.eof()) { //将文件中的信息运用后插法插入到链表中
		p = new LNode;//生成新结点
		file >> p->data.no >> p->data.name >> p->data.price;//输入元素值赋给新结点*p的数据域
		p->next = NULL;
		r->next = p;//将新结点*p插入尾结点*r之后 
		r = p;//r指向新的尾结点*p
		length++; //同时对链表长度进行统计
	}
	file.close();
} 

3、元素的查找(查找第 i 个元素 / 查找值为 e 的元素  )

Status GetElem_L(LinkList L, int i, Book &e) { //单链表的取值
//在带头结点的单链表L中查找第i个元素
//用e返回L中第i个数据元素的值
	int j;
	LinkList p;
	p = L->next;
	j = 1; //初始化,p指向第一个结点,j为计数器
	while (j < i && p) { //顺链域向后扫描,直到p指向第i个元素或p为空
	p = p->next; //p指向下一个结点
	++j; //计数器j相应加1
	}
	if (!p || j > i)
	return ERROR; //i值不合法i>n或i<=0
	e = p->data; //取第i个结点的数据域
	return OK;
} 


LNode *LocateElem_L_no(LinkList L,int e) { // 按书号查找
//在带头结点的单链表L中查找值为e的元素
	LinkList p;
	p = L->next;
	while (p && p-> data.no!=e)//顺链域向后扫描,直到p为空或p所指结点的数据域等于e
	p = p->next; //p指向下一个结点
	return p; //查找成功返回值为e的结点地址p,查找失败p为NULL 
}

4、单链表的插入

Status ListInsert_L(LinkList &L, int i, Book &e) { //单链表的插入
//在带头结点的单链表L中第i个位置插入值为e的新结点
	int j;
	LinkList p, s;
	p = L;
	j = 0;
	while (p && j < i - 1) {
		p = p->next;
		++j;
	}//查找第i个结点,p指向该结点
	if (!p || j > i - 1)
		return ERROR; //i>n+1或者i<1
		s = new LNode; //生成新结点*s 
		s->data = e; //将结点*s的数据域置为e
		s->next = p->next; //将结点*s的指针域指向结点ai
		p->next = s; //将结点*p的指针域指向结点*s
		++length;
		return OK;
} 

5、单链表的删除

Status ListDelete_L(LinkList &L, int i) { // 单链表的删除
//在带头结点的单链表L中,删除第i个位置
	LinkList p, q;
	int j;
	p = L;
	j = 0;
	while ((p->next) && (j < i - 1)) //查找第i个结点,p指向该结点
	{	
		p = p->next;
		++j;
	}
	if (!(p->next) || (j > i - 1))
	return ERROR; //当i>n或i<1时,删除位置不合理 
	q = p->next; //临时保存被删结点的地址以备释放 
	p->next = q->next; //改变删除结点前驱结点的指针域 
	delete q; //释放删除结点的空间 
	--length;
	return OK;
} 

2、按价格升序排序图书

在这里,我们选择冒泡排序。冒泡排序的英文Bubble Sort,是一种最基础的交换排序。

大家一定都喝过汽水,汽水中常常有许多小小的气泡,哗啦哗啦飘到上面来。这是因为组成小气泡的二氧化碳比水要轻,所以小气泡可以一点一点向上浮动。而我们的冒泡排序之所以叫做冒泡排序,正是因为这种排序算法的每一个元素都可以像小气泡一样,根据自身大小,一点一点向着数组的一侧移动。

冒泡排序算法的原理如下:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。

  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。

  3. 针对所有的元素重复以上的步骤,除了最后一个。

  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

  5. (要确保每次操作后,最后一个数字一定是最大的。)

void BubbleSort(LinkList L) {   //冒泡排序
    int i, j;
    LinkList p, q;
    double temp;
    for (i = 0; i < length - 1; i++) {
        p = L->next; 
        q = L->next->next;  //p的后一个元素
        for (j = 0; j < length - 1 - i; j++) {
            if (p->data.price > q->data.price) { //若前面的数字大则交换
                temp = p->data.price;    
                p->data.price = q->data.price;
                q->data.price = temp;
            }
            p = p->next;
            q = q->next;
        }
    }
}

3、按价格区间查找图书

此为基础操作。只需要在循环里判断图书的价格是否在循环里,如果在的话输出即可。

            double minPrice;
		    cout << "请输入价格区间的最小值和最大值(用空格隔开):";
		    cin >> minPrice >> maxPrice;
		    p = L->next;
		    while (p) {
		        if (p->data.price >= minPrice && p->data.price <= maxPrice) {
		            cout << left << setw(15) << p->data.no << "\t" << left << setw(50) <<                         
                    p->data.name << "\t" << left << setw(5) << p->data.price << endl;
		            found = true;
		        }
		        p = p->next;
		    }
		    if (!found) {
		        cout << "未找到符合价格区间的图书\n";
		    }
		    cout << endl;

4、根据指定书名,进行图书价格的修改

此处首先要做的就是先根据书名定位到相应的图书(与上述查找的逻辑相似)。找到后修改即可。

            cout << "请输入要修改价格的图书名称:";
		    cin >> name;
		    p = LocateElem_L_name(L, name);
		    if (p) {
		        cout << "请输入新的价格:";
		        cin >> p->data.price;
		        cout << "图书价格修改成功\n";
		        cout << "修改后的图书信息如下:\n";
		        cout << left << setw(15) << p->data.no << "\t" << left << setw(50) << p->data.name << "\t" << left << setw(5) << p->data.price << endl;
		    } else {
		        cout << "未找到该图书\n";
		    }
		    cout << endl;






LNode *LocateElem_L_name(LinkList L,string e) { // 按书名查找
//在带头结点的单链表L中查找值为e的元素
	LinkList p;
	p = L->next;
	while (p && p-> data.name!=e)//顺链域向后扫描,直到p为空或p所指结点的数据域等于e
	p = p->next; //p指向下一个结点
	return p; //查找成功返回值为e的结点地址p,查找失败p为NULL 
} //LocateElem_L

5、图书价格普调

此处按照前面所提到的普调的含义。当图书价格小于全部图书价格的平均值时乘1.2,大于的时候乘1.1即可。

            p = L->next;
		    while (p) {
		        sum += p->data.price;
		        count++;
		        p = p->next;
		    }
		    p = L->next;
		    while (p) {
		        if (p->data.price < average) {
		            p->data.price *= 1.2;
		        } else {
		            p->data.price *= 1.1;
		        }
		        p = p->next;
		    }
		    cout << "图书价格普调成功\n";
		    cout << "普调后的图书信息如下:\n";
		    p = L->next;
		    while (p) {
		        cout << left << setw(15) << p->data.no << "\t" << left << setw(50) << p->data.name << "\t" << left << setw(5) << p->data.price << endl;
		        p = p->next;
		    }
		    cout << endl;

6、查找最贵图书

此处用一个循环去判断它是否时最大的就好啦。

            Book maxBook;
			p = L->next;
			while (p) {
			    if (p->data.price > maxPrice) {
			        maxPrice = p->data.price;
			        maxBook = p->data;
			    }
			    p = p->next;
			}
			cout << "最贵的图书信息如下:\n";
			cout << left << setw(15) << maxBook.no << "\t" << left << setw(50) << maxBook.name << "\t" << left << setw(5) << maxBook.price << endl;
			cout << endl;

7、 图书去重

此处的意思是,当书名相同的时候,只保留第一个。

void RemoveDuplicates(LinkList &L) {
    LinkList p, q, r;
    p = L->next;
    while (p) {
        q = p;
        while (q->next) {
            if (q->next->data.no == p->data.no) {   //书名相同,保留第一个
                r = q->next;
                q->next = r->next;
                delete r;
            } else {
                q = q->next;
            }
        }
        p = p->next;
    }
}

8、完整代码

#include<iostream>
#include<string.h>
#include<iomanip>
#include<fstream>
using namespace std;

#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status; 
typedef int ElemType;

struct Book {
	char name[20];
	int no;
	double price;
};

typedef struct LNode {
	Book data; //结点的数据域
	struct LNode *next; //结点的指针域
	} LNode, *LinkList; //LinkList为指向结构体LNode的指针类型

string head_1, head_2, head_3;
int length;


Status InitList_L(LinkList &L) { 
//构造一个空的单链表L
	L = new LNode; //生成新结点作为头结点,用头指针L指向头结点
	L->next = NULL; //头结点的指针域置空
	return OK;
}

Status GetElem_L(LinkList L, int i, Book &e) { //单链表的取值
//在带头结点的单链表L中查找第i个元素
//用e返回L中第i个数据元素的值
	int j;
	LinkList p;
	p = L->next;
	j = 1; //初始化,p指向第一个结点,j为计数器
	while (j < i && p) { //顺链域向后扫描,直到p指向第i个元素或p为空
	p = p->next; //p指向下一个结点
	++j; //计数器j相应加1
	}
	if (!p || j > i)
	return ERROR; //i值不合法i>n或i<=0
	e = p->data; //取第i个结点的数据域
	return OK;
} //GetElem_L

LNode *LocateElem_L_name(LinkList L,string e) { // 按书名查找
//在带头结点的单链表L中查找值为e的元素
	LinkList p;
	p = L->next;
	while (p && p-> data.name!=e)//顺链域向后扫描,直到p为空或p所指结点的数据域等于e
	p = p->next; //p指向下一个结点
	return p; //查找成功返回值为e的结点地址p,查找失败p为NULL 
} //LocateElem_L

LNode *LocateElem_L_no(LinkList L,int e) { // 按书号查找
//在带头结点的单链表L中查找值为e的元素
	LinkList p;
	p = L->next;
	while (p && p-> data.no!=e)//顺链域向后扫描,直到p为空或p所指结点的数据域等于e
	p = p->next; //p指向下一个结点
	return p; //查找成功返回值为e的结点地址p,查找失败p为NULL 
} //LocateElem_L

Status ListInsert_L(LinkList &L, int i, Book &e) { //单链表的插入
//在带头结点的单链表L中第i个位置插入值为e的新结点
	int j;
	LinkList p, s;
	p = L;
	j = 0;
	while (p && j < i - 1) {
		p = p->next;
		++j;
	}//查找第i个结点,p指向该结点
	if (!p || j > i - 1)
		return ERROR; //i>n+1或者i<1
		s = new LNode; //生成新结点*s 
		s->data = e; //将结点*s的数据域置为e
		s->next = p->next; //将结点*s的指针域指向结点ai
		p->next = s; //将结点*p的指针域指向结点*s
		++length;
		return OK;
} //ListInsert_L

Status ListDelete_L(LinkList &L, int i) { // 单链表的删除
//在带头结点的单链表L中,删除第i个位置
	LinkList p, q;
	int j;
	p = L;
	j = 0;
	while ((p->next) && (j < i - 1)) //查找第i个结点,p指向该结点
	{	
		p = p->next;
		++j;
	}
	if (!(p->next) || (j > i - 1))
	return ERROR; //当i>n或i<1时,删除位置不合理 
	q = p->next; //临时保存被删结点的地址以备释放 
	p->next = q->next; //改变删除结点前驱结点的指针域 
	delete q; //释放删除结点的空间 
	--length;
	return OK;
} //ListDelete_L

void CreateList_H(LinkList &L, int n) { //算法2.11 创建单链表
//逆位序输入n个元素的值,建立到头结点的单链表L
	LinkList p;
	L = new LNode;
	L->next = NULL; //先建立一个带头结点的空链表
	length = 0;
	fstream file;
	file.open("Book.txt");
	if (!file) {
		cout << "未找到相关文件,无法打开!" << endl;
		exit(ERROR);
	}
	file >> head_1 >> head_2 >> head_3;
	while (!file.eof()) {
		p = new LNode; //生成新结点*p
		file >> p->data.no >> p->data.name >> p->data.price; //输入元素值赋给新结点*p的数据域
		p->next = L->next;
		L->next = p; //将新结点*p插入到头结点之后
		length++;//同时对链表长度进行统计
	}
	file.close();
} //CreateList_F

void CreateList_R(LinkList &L, int n) { // 尾插法创建单链表
//正位序输入n个元素的值,建立带表头结点的单链表L 
	LinkList p, r;
	L = new LNode;
	L->next = NULL; //先建立一个带头结点的空链表
	r = L; //尾指针r指向头结点
	length = 0;
	fstream file; //打开文件进行读写操作
	file.open("Book.txt");
	if (!file) {
		cout << "未找到相关文件,无法打开!" << endl;
		exit(ERROR);
	}
	file >> head_1 >> head_2 >> head_3;
	while (!file.eof()) { //将文件中的信息运用后插法插入到链表中
		p = new LNode;//生成新结点
		file >> p->data.no >> p->data.name >> p->data.price;//输入元素值赋给新结点*p的数据域
		p->next = NULL;
		r->next = p;//将新结点*p插入尾结点*r之后 
		r = p;//r指向新的尾结点*p
		length++; //同时对链表长度进行统计
	}
	file.close();
} //CreateList_L



void BubbleSort(LinkList L) {
    int i, j;
    LinkList p, q;
    double temp;
    for (i = 0; i < length - 1; i++) {
        p = L->next;
        q = L->next->next;
        for (j = 0; j < length - 1 - i; j++) {
            if (p->data.price > q->data.price) {
                temp = p->data.price;
                p->data.price = q->data.price;
                q->data.price = temp;
            }
            p = p->next;
            q = q->next;
        }
    }
}



void RemoveDuplicates(LinkList &L) {
    LinkList p, q, r;
    p = L->next;
    while (p) {
        q = p;
        while (q->next) {
            if (q->next->data.no == p->data.no) {
                r = q->next;
                q->next = r->next;
                delete r;
            } else {
                q = q->next;
            }
        }
        p = p->next;
    }
}


int main() {
	int a, n, choose;
	char name[20];
	int no;
	Book e;
	bool found = false;
	 int count = 0;
	 double sum = 0;
	 double average = sum / count;
	 double maxPrice = 0;
	LinkList L, p;
	cout << "==========主菜单=========\n";
	cout << "\t0. 退出\n";
	cout << "\t1. 图书的创建和输出\n";
	cout << "\t2. 新书入库(插入)\n";
	cout << "\t3. 旧书出库(删除)\n";
	cout << "\t4. 按书号查找图书\n";
	cout << "\t5. 按价格升序排序图书\n";
	cout << "\t6. 按按价格区间查找图书\n";
	cout << "\t7. 根据指定书名,进行图书价格的修改\n";
	cout << "\t8. 图书价格普调\n";
	cout << "\t9. 查找最贵图书\n";
	cout << "\t10. 图书去重\n";

	choose = -1;
	while (choose != 0) {
		cout << "请选择:";
		cin >> choose;
		switch (choose) {
			case 1: //图书的创建和输出
			if (InitList_L(L))
			cout << "成功建立链表!\n";
			CreateList_R(L, length);
			cout << "当前图书管理系统信息(链表)输出:\n";
			p = L->next;
			while (p) {
			cout << left << setw(15) << p->data.no << "\t" << left << setw(
			50) << p->data.name << "\t" << left << setw(5)
			<< p->data.price << endl;
			p = p->next;
			}
			cout << endl;
			break;
			
			case 2:  //新书入库(插入)
			cout << "请输入插入的位置和图书的信息,包括:书号 书名 价格(用空格隔开):";
			cin >> a;
			cin >> e.no >> e.name >> e.price;
			if (ListInsert_L(L, a, e)){ 
			cout << "插入成功.\n\n";
			cout << "当前图书管理系统信息(链表)输出:\n";
			p = L->next;
			while (p) {
			cout << left << setw(15) << p->data.no << "\t" << left << setw(
			50) << p->data.name << "\t" << left << setw(5)
			<< p->data.price << endl;
			p = p->next;
			}
			cout << endl;
			} 
			else{ 
			cout << "插入失败!\n\n";
			} 
			break;
			
		
			case 3: //旧书出库(删除)
			cout << "请输入所要删除的图书信息的位置:";
			cin >> a;
			if (ListDelete_L(L, a))
			cout << "删除成功!\n\n";
			else
			cout << "删除失败!\n\n";
			break;
			
			
			case 4: //按书号查找图书
			cout << "请输入所要查找书号:";
			cin >> no;
			if (LocateElem_L_no(L,no) != NULL) {
			cout << "查找成功\n";
			cout << "该图书对应的书名为:" << LocateElem_L_no(L, no)->data.name
			<< endl << endl;
			cout << "该图书对应的价格为:" << LocateElem_L_no(L, no)->data.price
			<< endl << endl;
			} else
			cout << "查找失败! 该图书" <<no << " 没有找到\n\n";
			break;
		
//			case 5: //按价格升序排序图书
//			sort(L); 
//			cout << "按价格升序排序图书如下:\n";
//			p = L->next;
//			while (p) {
//			cout << left << setw(15) << p->data.no << "\t" << left << setw(
//			50) << p->data.name << "\t" << left << setw(5)
//			<< p->data.price << endl;
//			p = p->next;
//			}
//			cout << endl;
//			break;

			case 5: //按价格升序排序图书
		    BubbleSort(L);
		    cout << "按价格升序排序后的图书管理系统信息(链表)输出:\n";
		    p = L->next;
		    while (p) {
		        cout << left << setw(15) << p->data.no << "\t" << left << setw(50) << p->data.name << "\t" << left << setw(5) << p->data.price << endl;
		        p = p->next;
			}
		    cout << endl;
		    break;
			
			case 6: //按按价格区间查找图书
		    double minPrice;
		    cout << "请输入价格区间的最小值和最大值(用空格隔开):";
		    cin >> minPrice >> maxPrice;
		    p = L->next;
		    while (p) {
		        if (p->data.price >= minPrice && p->data.price <= maxPrice) {
		            cout << left << setw(15) << p->data.no << "\t" << left << setw(50) << p->data.name << "\t" << left << setw(5) << p->data.price << endl;
		            found = true;
		        }
		        p = p->next;
		    }
		    if (!found) {
		        cout << "未找到符合价格区间的图书\n";
		    }
		    cout << endl;
		    break;
		
			case 7: //根据指定书名,进行图书价格的修改
		    cout << "请输入要修改价格的图书名称:";
		    cin >> name;
		    p = LocateElem_L_name(L, name);
		    if (p) {
		        cout << "请输入新的价格:";
		        cin >> p->data.price;
		        cout << "图书价格修改成功\n";
		        cout << "修改后的图书信息如下:\n";
		        cout << left << setw(15) << p->data.no << "\t" << left << setw(50) << p->data.name << "\t" << left << setw(5) << p->data.price << endl;
		    } else {
		        cout << "未找到该图书\n";
		    }
		    cout << endl;
		    break;

			
			case 8: //图书价格普调
		    p = L->next;
		    while (p) {
		        sum += p->data.price;
		        count++;
		        p = p->next;
		    }
		    p = L->next;
		    while (p) {
		        if (p->data.price < average) {
		            p->data.price *= 1.2;
		        } else {
		            p->data.price *= 1.1;
		        }
		        p = p->next;
		    }
		    cout << "图书价格普调成功\n";
		    cout << "普调后的图书信息如下:\n";
		    p = L->next;
		    while (p) {
		        cout << left << setw(15) << p->data.no << "\t" << left << setw(50) << p->data.name << "\t" << left << setw(5) << p->data.price << endl;
		        p = p->next;
		    }
		    cout << endl;
   			break;

			
			case 9: //查找最贵图书
			Book maxBook;
			p = L->next;
			while (p) {
			    if (p->data.price > maxPrice) {
			        maxPrice = p->data.price;
			        maxBook = p->data;
			    }
			    p = p->next;
			}
			cout << "最贵的图书信息如下:\n";
			cout << left << setw(15) << maxBook.no << "\t" << left << setw(50) << maxBook.name << "\t" << left << setw(5) << maxBook.price << endl;
			cout << endl;
			break;
			

			case 10: //图书去重
		    RemoveDuplicates(L);
		    cout << "图书去重成功\n";
		    cout << "去重后的图书信息如下:\n";
		    p = L->next;
		    while (p) {
		        cout << left << setw(15) << p->data.no << "\t" << left << setw(50) << p->data.name << "\t" << left << setw(5) << p->data.price << endl;
					 p = p->next;
			}
				break;
		}
	}
return 0;
}