线性表链式储存(图书管理系统)

      线性表链式储存和顺序储存各有优点

“该笔记的一些说法是自己的理解,并不官方”

      首先我们要创建一个结构体用来储存书籍的相关属性信息,我称为数据结构体(储存一组待储存的数据)

typedef struct book
{
	string bnum;//书的编号
	string bname;//书名
	float bprice;//书的价格
}book;//书籍结构体

 

    然后再创建一个结构体用来作为“表格”其中的成员有数据域和指针域(注意这只是一个表格)

typedef struct Node
{
	ElemType M_e;//数据域
	struct Node* next;//指针域
}Node;//表格结构体

      一个个的表格连接起来就是链表,所以链表就是一个表格指向另一个表格,一直连续不断的指下去

typedef struct Node* LinkList;//链表

 

    1. 表格定义好后我们便要初始化链表,初始化链表函数如下:

Status InitList(LinkList*L)//初始化链表(创造头结点)
{
	*L = (LinkList)malloc(sizeof(Node));//开辟空间,L是个双重指针所以这里要解引用
	if (!(*L))
		return 0;
	(*L)->next = NULL;
	return 1;
}

      初始化链表其实就是创造头结点的过程(头结点其实也就是一个表格,只是里面的数据域没有信息,只有指针域指向第一个结点)首先开辟一个表格的空间,判断是否开辟成功,开辟成功后由于此时链表还没有任何数据,所以初始化头结点指针域的指针指向空。

      2.链表初始化好以后我们便可以定义向链表输入数据的函数creatListTail,过程如下:

void creatListTail(LinkList* L)
{
	LinkList p,r;//p代表新输入数据的位置,r代表最后一个数据所在的位置
	r = (*L);//初始化r
	cout << "输入完毕按0退出" << endl;
	while (1)
	{
		p = new Node;
		/*向数据区输入数据*/
		
		cout << "请输入书的编号" << endl;
		cin >> p->M_e.bnum;
		if (p->M_e.bnum == "0")
			break;
		cout << "请输入书名" << endl;
		cin >> p->M_e.bname;
		cout << "请输入书的价格" << endl;
		cin >> p->M_e.bprice;
		r->next = p;//把p接在r的后面
		r = p;//此时p变为了最后一位,更新r为p
	}
	r->next = NULL;//r是最后一位所以指向空指针
}

      这里我们使用的是尾插法,要定义一个表格指针r来一直指向链表最后一个表格的位置,便于将用户新输入的数据连到表格的最后,一开始表格内没有数据,所以r一开始初始化为指向头结点(*L)

用p来指向开辟的一个表格(也可以说是结点)的空间,将用户输入的数据储存到开辟的这个空间中,输入完毕后将新开辟的表格p连到r的后面,此时最后一位变成了p于是让r指向此时p所在的位置,记录最后一个表格的位置(最后一个表格后面肯定没有表格了所以将r的指针域指向空)

      3.将数据输入到链表中后我们要将其输出,定义输出函数PrintdList如下:

void PrintdList(LinkList* L)//输出所有的数据
{
	LinkList p;//用来遍历的指针
	p = (*L)->next;//让p指向第一个结点
	if (p == NULL)
	{
		cout << "链表没有数据哦" << endl;
		return;
	}
	while (p)
	{
		cout << "编号:" << p->M_e.bnum << endl;
		cout << "书名:" << p->M_e.bname << endl;
		cout << "价格:" << p->M_e.bprice << endl;
		cout << endl;
		p = p->next;//打印出当前结点的数据后遍历到下一个结点
	}
}

      我们要输出所有表格的数据便要遍历所有表格,将遍历用的指针p初始化指向第一个表格后,判断第一个表格是否存在,没有存在就说明链表中没有数据。通过while循环不停的遍历并输出链表中的信息,直到指针p指向的位置没有数据为止。

      4.我们接下来还需要有查询用户输入数据的功能,查询功能的函数 GetElem如下:

void GetElem(LinkList* L)//查询用户输入的数据
{
	int a = 0;
	string name;
	string num;//定义一个临时变量来储存用户输入的数据
	LinkList p=(*L)->next;//用来遍历链表;对p初始化
	if (p == NULL)
	{
		cout << "链表没有数据哦" << endl;
		return;
	}
	cout << "1.查询书名  2.查询书籍编号" << endl;
	cin >> a;
	switch (a)
	{
	case 1:
	{
		int dbd=1;
		cout << "请输入您要查询的书名" << endl;
		cin >> name;
		do
		{
			if (p->M_e.bname == name)
			{
				dbd = 0;//找到数据退出循环
			}
			else
				p = p->next;//不是当前数据,继续向下遍历
		} while (dbd && p);
		if (dbd == 0)
		{
			cout << "编号:" << p->M_e.bnum << endl;
			cout << "书名:" << p->M_e.bname << endl;
			cout << "价格:" << p->M_e.bprice << endl;
		}
		else
			cout << "没有查到相关数据" << endl;
		
		break;
	}
	case 2:
	{
		int dbd = 1;
		cout << "请输入您要查询的书籍编号" << endl;
		cin >> num;
		do
		{
			if (p->M_e.bnum == num)
			{
				dbd = 0;//找到数据退出循环
			}
			else
				p = p->next;//不是当前数据,继续向下遍历
		} while (dbd && p);
		if (dbd == 0)
		{
			cout << "编号:" << p->M_e.bnum << endl;
			cout << "书名:" << p->M_e.bname << endl;
			cout << "价格:" << p->M_e.bprice << endl;
		}
		else
			cout << "没有查到相关数据" << endl;
		break;
	}
	}
}

      查询这里我们给了用户两种方法查询,一种通过书名查询,一种通过编号查询,两个过程差别不大,我们以查询书名为例:首先依然需要判断链表中是否有数据,避免做无用功。定义一个string类型的变量来储存用户输入的书名,在do  while循环中将找到相关数据后退出循环的功能表现为改变dbd的值能更灵活。由于退出while循环的条件有找到相关数据后退出和p指针指向空后退出(链表内的数据全部遍历完)这两种情况,所以在后面要判断是哪一种情况退出的,当dbd为0就说明是因为找到了相关数据退出的,于是便输出此时p指向的表格内的数据,dbd不为0,就说明是因为遍历完链表退出的(没有找到相关数据)

      5.接下来我们实现向链表指定位置插入数据的功能,函数ListInsert1和ListInsert2如下:

ListInsert1:

LinkList ListInsert1(LinkList* L, int i)//遍历到i-1的位置,并对一些特殊情况进行判断
{
	LinkList p;
	p = *L;
	int j = 0;//用于计数
	/*这里用while循环可以判断i值是否是合法输入,for循环就不行*/
	while (p && j < i - 1)//将p指向i-1的位置(要在i处插入i-1处必须存在结点而i处随意)
	{
		p = p->next;
		j++;
	}
	if (!p || j > i - 1)//判断是因为什么情况退出的while循环(没有查找到i处的情况)
	{
		cout << "无法插入到" << i << "处" << endl;
		return NULL;
	}
	return p;
}

      ListInsert2:

void ListInsert2(LinkList p, ElemType e)
{
	LinkList s;
	s = new Node;//开辟新结点
	s->M_e = e;
	s->next = p->next;
	p->next = s;
	cout << "插入成功" << endl;
}

      函数ListInsert1是用于返回i-1处的位置,因为要在i处插入数据,那么i-1处就必须要有数据,所以用ListInsert1函数返回i-1的位置并且可以判断i-1处是否有数据,要是没有数据便说明无法插入,可以直接退出程序避免多余的操作。

      要是ListInsert1成功返回i-1处的位置,我们便可以向程序输入数据,并调用ListInsert2函数将数据输入链表

      6.接下来我们实现删除链表指定位置数据的功能,函数DeleteList1和DeleteList2如下:

DeleteList1:

LinkList DeleteList1(LinkList* L, int i)//遍历到i-1的位置,并对一些特殊情况进行判断
{
	int j = 0;//用于计数
	LinkList p;//用p来遍历到i处
	p = *L;
	while (p->next && j < i - 1)//遍历将p指向i-1处
	{
		p = p->next;
		j++;
	}
	if (!(p->next) || j > i - 1)//离开while循环p却没有指向i-1处的情况
	{
		cout << "无法删除" << i << "处的书籍" << endl;
		return NULL;
	}
	
	return p;//此时p指向i-1处的位置
}

DeleteList2:

void DeleteList2(LinkList p)//删除链表i处的数据
{
	LinkList q;
	q = p->next;//记录即将删除的结点的地址便于一会释放
	cout << "您删除的书籍为:" <<q->M_e.bname<< endl;
	p->next = q->next;
	free(q);

}

      我们这里的DeleteList1和查询函数ListInsert1差别不大,都是返回i-1处的位置,虽然乍一看i-1处的数据存在与否和删除i处的数据没有太大关系,但p要指向i-1处的位置才好进行链表删除操作时指针域数据的改变。

      7.接下来我们实现清空链表的功能,函数ClearList如下:

Status ClearList(LinkList* L)
{
	LinkList p, q;
	p = (*L)->next;
	while (p)
	{
		q = p->next;
		free(p);
		p = q;
	}
	(*L)->next = NULL;
	cout << "链表已清空" << endl;
	return 1;
}

      用两个表格指针交替遍历便可以清空链表

接下来便是所有代码展示:

头文件FUNC.h

#pragma once
#include <iostream>
#include<string>
using namespace std;


typedef int Status;//Status代表函数类型,这里暂设定为int


typedef struct book
{
	string bnum;//书的编号
	string bname;//书名
	float bprice;//书的价格
}book;//书籍结构体


typedef book ElemType;


typedef struct Node
{
	ElemType M_e;//数据域
	struct Node* next;//指针域
}Node;//表格结构体
typedef struct Node* LinkList;//链表


Status InitList(LinkList*L);//初始化链表


Status ClearList(LinkList* L);//清空链表


void creatListTail(LinkList* L);//向链表输入数据


void PrintdList(LinkList* L);//输出所有的数据


void GetElem(LinkList* L);//查询用户输入数据的信息


LinkList ListInsert1(LinkList* L, int i);//在链表的i处插入数据
void ListInsert2(LinkList p, ElemType e);


LinkList DeleteList1(LinkList* L, int i);//删除链表i处的数据
void DeleteList2(LinkList p);

源文件FUNC.cpp

#include "FUNC.h"


Status InitList(LinkList*L)//初始化链表(创造头结点)
{
	*L = (LinkList)malloc(sizeof(Node));//开辟空间,L是个双重指针所以这里要解引用
	if (!(*L))
		return 0;
	(*L)->next = NULL;
	return 1;
}


/* 初始条件:链式线性表L已存在。操作结果:将L重置为空表 */
Status ClearList(LinkList* L)
{
	LinkList p, q;
	p = (*L)->next;
	while (p)
	{
		q = p->next;
		free(p);
		p = q;
	}
	(*L)->next = NULL;
	cout << "链表已清空" << endl;
	return 1;
}


/*向链表输入数据*/
void creatListTail(LinkList* L)
{
	LinkList p,r;//p代表新输入数据的位置,r代表最后一个数据所在的位置
	r = (*L);//初始化r
	cout << "输入完毕按0退出" << endl;
	while (1)
	{
		p = new Node;
		/*向数据区输入数据*/
		
		cout << "请输入书的编号" << endl;
		cin >> p->M_e.bnum;
		if (p->M_e.bnum == "0")
			break;
		cout << "请输入书名" << endl;
		cin >> p->M_e.bname;
		cout << "请输入书的价格" << endl;
		cin >> p->M_e.bprice;
		r->next = p;//把p接在r的后面
		r = p;//此时p变为了最后一位,更新r为p
	}
	r->next = NULL;//r是最后一位所以指向空指针
}


void PrintdList(LinkList* L)//输出所有的数据
{
	LinkList p;//用来遍历的指针
	p = (*L)->next;//让p指向第一个结点
	if (p == NULL)
	{
		cout << "链表没有数据哦" << endl;
		return;
	}
	while (p)
	{
		cout << "编号:" << p->M_e.bnum << endl;
		cout << "书名:" << p->M_e.bname << endl;
		cout << "价格:" << p->M_e.bprice << endl;
		cout << endl;
		p = p->next;//打印出当前结点的数据后遍历到下一个结点
	}
}


void GetElem(LinkList* L)//查询用户输入的数据
{
	int a = 0;
	string name;
	string num;//定义一个临时变量来储存用户输入的数据
	LinkList p=(*L)->next;//用来遍历链表;对p初始化
	if (p == NULL)
	{
		cout << "链表没有数据哦" << endl;
		return;
	}
	cout << "1.查询书名  2.查询书籍编号" << endl;
	cin >> a;
	switch (a)
	{
	case 1:
	{
		int dbd=1;
		cout << "请输入您要查询的书名" << endl;
		cin >> name;
		do
		{
			if (p->M_e.bname == name)
			{
				dbd = 0;//找到数据退出循环
			}
			else
				p = p->next;//不是当前数据,继续向下遍历
		} while (dbd && p);
		if (dbd == 0)
		{
			cout << "编号:" << p->M_e.bnum << endl;
			cout << "书名:" << p->M_e.bname << endl;
			cout << "价格:" << p->M_e.bprice << endl;
		}
		else
			cout << "没有查到相关数据" << endl;
		
		break;
	}
	case 2:
	{
		int dbd = 1;
		cout << "请输入您要查询的书籍编号" << endl;
		cin >> num;
		do
		{
			if (p->M_e.bnum == num)
			{
				dbd = 0;//找到数据退出循环
			}
			else
				p = p->next;//不是当前数据,继续向下遍历
		} while (dbd && p);
		if (dbd == 0)
		{
			cout << "编号:" << p->M_e.bnum << endl;
			cout << "书名:" << p->M_e.bname << endl;
			cout << "价格:" << p->M_e.bprice << endl;
		}
		else
			cout << "没有查到相关数据" << endl;
		break;
	}
	}
}


/*在链表的i处插入数据*/
LinkList ListInsert1(LinkList* L, int i)//遍历到i-1的位置,并对一些特殊情况进行判断
{
	LinkList p;
	p = *L;
	int j = 0;//用于计数
	/*这里用while循环可以判断i值是否是合法输入,for循环就不行*/
	while (p && j < i - 1)//将p指向i-1的位置(要在i处插入i-1处必须存在结点而i处随意)
	{
		p = p->next;
		j++;
	}
	if (!p || j > i - 1)//判断是因为什么情况退出的while循环(没有查找到i处的情况)
	{
		cout << "无法插入到" << i << "处" << endl;
		return NULL;
	}
	return p;
}
void ListInsert2(LinkList p, ElemType e)
{
	LinkList s;
	s = new Node;//开辟新结点
	s->M_e = e;
	s->next = p->next;
	p->next = s;
	cout << "插入成功" << endl;
}


/*删除链表i处的数据*/
LinkList DeleteList1(LinkList* L, int i)//遍历到i-1的位置,并对一些特殊情况进行判断
{
	int j = 0;//用于计数
	LinkList p;//用p来遍历到i处
	p = *L;
	while (p->next && j < i - 1)//遍历将p指向i-1处
	{
		p = p->next;
		j++;
	}
	if (!(p->next) || j > i - 1)//离开while循环p却没有指向i-1处的情况
	{
		cout << "无法删除" << i << "处的书籍" << endl;
		return NULL;
	}
	
	return p;//此时p指向i-1处的位置
}
void DeleteList2(LinkList p)//删除链表i处的数据
{
	LinkList q;
	q = p->next;//记录即将删除的结点的地址便于一会释放
	cout << "您删除的书籍为:" <<q->M_e.bname<< endl;
	p->next = q->next;
	free(q);

}
	

源文件text.cpp

#include "FUNC.h"
int main()
{
	cout << "欢迎来到图书馆管理系统(链式储存)" << endl;
	int b1 = 0;
	LinkList L;//定义一个数据表L出来
	if (!InitList(&L))//用了Initlist函数已经初始化链表
		cout << "链表创建失败" << endl;
	int APS = 1;
	while (APS)//用APS方便后面退出while循环
	{
		system("pause");
		system("cls");
		cout << "       请选择您要进行的操作" << endl;
		cout << "1.存储书籍,2.输出所有书籍  3.查询书籍  4.插入书籍  5.删除书籍  6.退出程序  7.删除所有书籍" << endl;
		cin >> b1;
		switch (b1)
		{
		case 1:
		{
			creatListTail(&L);
			break;
		}
		case 2:
		{
			PrintdList(&L);
			break;
		}
		case 3:
		{
			GetElem(&L);
			break;
		}
		case 4:
		{
			ElemType e;//用一个临时变量储存用户输入的数据
			LinkList p;//用临时变量储存函数ListInsert1的返回值
			int i;
			cout << "请输入在哪里插入书籍" << endl;
			cin >> i;
			p = ListInsert1(&L, i);
			if (p)
			{
				cout << "请输入书籍编号" << endl;
				cin >> e.bnum;
				cout << "请输入书名" << endl;
				cin >> e.bname;
				cout << "请输入价格" << endl;
				cin >> e.bprice;
				ListInsert2(p, e);//用ListInsert2函数将数据输入链表

			}
			break;
		}
		case 5:
		{
			LinkList p;//用来接收函数DeleteList1返回的指针
			ElemType e;//用来储存删除的数据
			int i;
			int verify;//用于确认是否删除数据
			cout << "要删除哪个位置的数据" << endl;
			cin >> i;
			p=DeleteList1(&L, i);
			cout << "要删除的书籍为:" << p->next->M_e.bname << endl;//p指向的是i-1处的数据而不是i处的,所以要用 p->next->M_e.bname来输出i处的书名
			cout << "是否确认删除:1.是  2.否" << endl;
			cin >> verify;
			if (verify == 1)
			{
				DeleteList2(p);
			}
			else if (verify == 2)
			{
				cout << "取消删除" << endl;
			}
			else
				cout << "非法输入" << endl;
			break;
		}

		case 6:
		{
			cout << "感谢使用" << endl;
			APS = 0;
			break;
		}
		case 7:
		{
			int verify;
			cout << "是否要清空链表:1.是  2.否" << endl;
			cin >> verify;
			if (verify == 1)
			{
				ClearList(&L);
			}
			else if (verify == 2)
			{
				cout << "取消清空" << endl;
			}
			else
				cout << "非法输入" << endl;
			break;
		}
		default:
		{
			cout << "该输入不合法" << endl;
			break;
		}
		}
	}
	return 0;
}

     线性表链式储存完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小林想被监督学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值