单链表的基本操作和实现

单链表的基本操作和实现

/**
 * @file SingleLinkList.cpp
 * @author qq_45671732 (2324144691@qq.com)
 * @brief
 * @version 0.1
 * @date 2022-08-16
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "iostream"

using namespace std;

#define TRUE 1

#define FALSE 0

//函数操作执行成功返回OK
#define OK 1

//函数操作执行失败返回ERROR
#define ERROR 0

typedef int Status;

typedef string ElemType;

/*
单链表的结点包含了一个存放数据的数据域 和 一个指向下一个LNode的指针域
LNode为声明的结构体的普通变量 *SingleLinkList为指向单链表结点的一个指针变量
*/
typedef struct LNode{
	ElemType elem;
	struct LNode* next;
}LNode,*SingleLinkList;

Status LinkList_Init(SingleLinkList& L);

Status LinkList_Destory(SingleLinkList& L);

Status LinkList_Clear(SingleLinkList& L);

int LinkList_GetLength(SingleLinkList L);

int LinkList_IsEmpty(SingleLinkList L);

Status LinkList_Insert(SingleLinkList& L, int i, ElemType e);

Status LinkList_Delete(SingleLinkList& L, int i);

Status LinkList_Ergodic(SingleLinkList L);

Status LinkList_CreateListForHead(SingleLinkList& L, int n);

Status LinkList_CreateListForEnd(SingleLinkList& L, int n);

Status LinkList_GetElem(SingleLinkList L, int i);

int LinkList_LocateElem(SingleLinkList L, ElemType e);

int main() {
	SingleLinkList L;
	LinkList_Init(L);
	int choice;
	do
	{
		cout << "1.销毁单链表" << endl;
		cout << "2.清空单链表" << endl;
		cout << "3.判断单链表是否为空" << endl;
		cout << "4.获取单链表的长度" << endl;
		cout << "5.单链表插入元素" << endl;
		cout << "6.单链表删除元素" << endl;
		cout << "7.头插法建立单链表" << endl;
		cout << "8.尾插法建立单链表" << endl;
		cout << "9.获取线性表L中位序为i的结点对应的内容" << endl;
		cout << "10.打印单链表" << endl;
		cout << "11.在单链表L中查找值为e的数据元素的位置序号" << endl;
		cout << "***************************************************" << endl;
		cout << "请输入您的选择:" << endl;
		cin >> choice;
		switch (choice)
		{
		case 1: {
			LinkList_Destory(L);
			exit(OK);
			break;
		}
		case 2: {
			LinkList_Clear(L);
			break;
		}
		case 3: {
			LinkList_IsEmpty(L);
			break;
		}
		case 4: {
			int length = 0;
			length = LinkList_GetLength(L);
			cout << "单链表的表长为:" << length << endl;
			break;
		}
		case 5: {
			ElemType e;
			int i = 0;
			cout << "请输入您要插入的结点的内容:" << endl;
			cin >> e;
			cout << "请输入您要插入的结点的位序:" << endl;
			cin >> i;
			LinkList_Insert(L, i, e);
			break;
		}
		case 6: {
			int i = 0;
			cout << "请输入您要删除的结点的位序:" << endl;
			cin >> i;
			LinkList_Delete(L, i);
			break;
		}
		case 7: {
			int n = 0;
			cout << "请输入您要用头插法创建单链表的结点的个数:" << endl;
			cin >> n;
			LinkList_CreateListForHead(L, n);
			break;
		}
		case 8: {
			int n = 0;
			cout << "请输入您要用尾插法创建单链表的结点的个数:" << endl;
			cin >> n;
			LinkList_CreateListForEnd(L, n);
			break;
		}
		case 9: {
			int i = 0;
			cout << "请输入您要查找的元素的位序:" << endl;
			cin >> i;
			LinkList_GetElem(L, i);
			break;
		}
		case 10: {
			LinkList_Ergodic(L);
			break;
		}
		case 11: {
			ElemType e;
			int i = 0;
			cout << "请输入您要查找的结点的内容:" << endl;
			cin >> e;
			i = LinkList_LocateElem(L, e);
			cout << "数据域为:" << e << "的结点的位序为:" << i << endl;
			break;
		}
		default: {
			exit(ERROR);
		}
		}
	} while (1);
	system("pause");
	return 0;
}

Status LinkList_Init(SingleLinkList& L) {
	/*
		单链表的初始化操作
		初始化操作是在内存中申请一块大小为sizeof(LNode)的存储空间 作为单链表的头结点
		将头结点的地址返回给头指针L
		初始化成功后需要将头结点的指针域设置为NULL 所以L->next = NULL;
	*/
	L = new LNode;//C语言中为L = (LNode *)malloc(sizeof(LNode));
	if (L != NULL)
	{
		//说明单链表的头结点创建成功 并将头结点的指针域设置为NULL 
		L->next = NULL;
		cout << "单链表初始化成功" << endl;
		return OK;
	}
	else
	{
		cerr << "ERROR" << endl;
		return ERROR;
	}
}

Status LinkList_Destory(SingleLinkList& L) {
	/*
		销毁单链表 
	*/
	LNode* p;
	/*
		销毁单链表时需要从头结点开始依次释放每一个结点
		指针p指向将要销毁的单链表结点的内存地址
		首先将p指针指向头结点 而头结点的地址存储在头指针L处 所以p = L;
		那么我们需要去释放头结点 但是释放头结点后 由于我们没有用一个指针指向首元结点的位置 那么首元结点我们无法找到
		所以将L指针指向首元结点 那么首元结点怎么找到呢? 
		由于单链表的结点在结构体设计时 存放了存储数据的数据域 和 指向下一个结点地址的指针域
		也就是说首元结点的地址存放在头结点的指针域中 L = L->next;
		指针L后移后,指针p指向头结点 就可以用(delete 指针)的方式去释放掉p指向的内存空间了
		释放了头结点后 我们需要释放单链表中其他的结点
		我们还是去做上面的几个操作 
		p = L;
		L = L->next;
		delete p;
		那么什么时候循环结束呢?
		当你在销毁结点后 遇到了尾结点 此时指针L已经指向了尾结点 p指针释放了尾结点的前驱结点
		p = L; p指向了尾结点
		L = L->next; 这里需要注意因为单链表的尾结点的指针域设置为NULL 所以到这一步时 指针L = NULL了
		此时用指针p将尾结点delete掉 这样保证了指针L和指针p都为NULL,不存在野指针
	*/
	while (L != NULL)
	{
		p = L;
		L = L->next;//L指针向后移动一个结点
		delete p;
	}
	cout << "单链表销毁成功" << endl;
	return OK;
}

Status LinkList_Clear(SingleLinkList& L) {
	/*
		清空单链表
		清空单链表的操作和销毁单链表的操作不一样 
		销毁单链表是将所有结点都依次释放掉 而清空单链表是将除了头结点的其他结点依次释放 
		释放完其他结点后 由于头结点后没有其他结点 需要将头结点的指针域设置为NULL
		第一步:
			指针p指向首元结点 首元结点的地址存储在头结点的指针域 p = L->next;此时首元结点成为了将要删除的结点
		第二步:
			指针q指向首元结点的后继结点 而后继结点的地址存储在首元结点的指针域中 所以q = p->next;
		第三步:
			使用delete的方式将指针p指向的结点释放掉
		第四步:
			指针p释放完首元结点后 要将指针p指向将要释放的首元结点的后继结点的地址 而后继结点的地址存放在指针q中 所以p = q;
		那么什么时候循环结束呢?
			当释放掉p指针指向的尾结点的前驱结点后 此时指针q已经指向了尾结点的内存空间
			p = q;指针p指向了尾结点
			q = p->next;将尾结点的指针域赋给了指针q 此时指针q = NULL;
			用delete的方式释放掉尾结点 指针p也为NULL 此时除了有头指针指向的头结点后 单链表不存在其他结点了 循环结束
		收尾工作:
			L->next = NULL;释放完其他结点后 由于头结点后没有其他结点 需要将头结点的指针域设置为NULL
	*/
	LNode* p, * q;//SingleLinkList p,q; 声明两个指向LNode类型的指针变量 其中指针p指向了要删除的LNode结点的地址
	p = L->next;
	while (p != NULL)
	{
		q = p->next;
		delete p;
		p = q;
	}
	L->next = NULL;
	cout << "单链表清空成功" << endl;
	return OK;
}

int LinkList_GetLength(SingleLinkList L) {
	/*
	获取单链表的表长
	获取单链表的表长是使用一个计数器i从首元结点开始依次遍历 每当遇到一个结点后计数器加1 循环结束后直接return i;即可
	第一步:
		指针p指向首元结点p = L->next; 创建一个计数器变量i 此时由于p指向了首元结点 所以i自增1
	第二步:
		指针p后移一个结点 也就是将p指向首元结点的后继结点 p = p->next;此时i再次自增1
	不断循环下去 一直到指针p指向了尾结点时 i自增1后 此时单链表中除去头结点的其他结点都已经计算进去了
	p = p->next;将尾结点的指针域赋给了指针p 此时p = NULL 循环就可以结束了
	*/
	SingleLinkList p;
	p = L->next;
	int i = 0;
	while (p != NULL)
	{
		i++;
		p = p->next;
	}
	return i;
}

int LinkList_IsEmpty(SingleLinkList L) {
	/*
		单链表判空操作
		如果头结点的指针域为NULL 说明没有结点 不为NULL说明存在结点
	*/
	if (L->next != NULL)
	{
		cout << "单链表不为空" << endl;
		return 0;
	}
	else {
		cout << "单链表为空" << endl;
		return 1;
	}
}

Status LinkList_Insert(SingleLinkList& L, int i, ElemType e) {
	/*
	单链表的插入操作
	单链表的插入操作是在单链表的第i个结点之前插入一个新结点 数据域为e
	在数据域为e的结点后 先将新结点的指针域指向第i - 1个结点的后继结点 然后将第i - 1的结点的指针域指向新结点的地址
	第一步:
		在单链表在第i个结点之前插入一个新结点 要先找到第i个结点 想要找到第i个结点 就需要找到第i - 1个结点
		所以需要从首元结点开始 依次访问每一个结点 直到第i个结点前停止
		while((p != NULL) && (j < i - 1)){
			当指针p不为空 并且 j < i - 1时 p = p->next后移一个结点 计数器j自增
			p = p->next;
			j++;
		}
		if ((p == NULL) || (j > i - 1))检查第一步操作是否成功
		此时指针p指向第i - 1个结点
	第二步:
		LNode* s = new LNode;通过new关键字去在内存中申请一块大小为sizeof(LNode)的内存空间 并转换为LNode类型
		将新申请的结点的内存地址赋给指针s 
		s->elem = e;为新结点的数据域进行赋值
	第三步:
		将数据域为e的新结点插入到第i个结点
		s->next = p->next;先将新结点的指针域指向第i - 1个结点的后继结点
		p->next = s;然后将第i - 1的结点的指针域指向新结点的地址
	*/
	SingleLinkList p;
	p = L;
	int j = 0;
	while ((p != NULL) && (j < i - 1))
	{
		p = p->next;
		j++;
	}
	if ((p == NULL) || (j > i - 1))
	{
		cout << "单链表插入失败" << endl;
		return ERROR;
	}
	LNode* s = new LNode;
	s->elem = e;
	s->next = p->next;
	p->next = s;
	cout << "单链表插入成功" << endl;
	return OK;
}

Status LinkList_Delete(SingleLinkList& L, int i) {
	/*
	单链表删除操作
	在单链表中删除第i个结点
	第一步:
		指针p指向头结点 
		while ((p->next != NULL) && (j < i - 1))将指针p指向第i - 1个结点
		if ((p->next == NULL) || (j > i - 1)) {检查这一步操作是否成功
	第二步:
		声明一个指针q指向第i个结点 第i个结点的位置存储在第i - 1的结点的指针域 指针p指向第i - 1结点
		SingleLinkList q = p->next;
		此时指针q指向第i个结点 要删除第i个结点 直接将第i - 1结点的指针域的值存储第i + 1结点的地址
		而第i + 1结点的地址存储在第i结点的指针域中 所以p->next = q->next;
		将指针q指向的第i个结点用delete删除
	*/
	SingleLinkList p;
	p = L;
	int j = 0;
	while ((p->next != NULL) && (j < i - 1))
	{
		p = p->next;
		++j;
	}
	if ((p->next == NULL) || (j > i - 1)) {
		cout << "单链表删除失败" << endl;
		return ERROR;
	}
	SingleLinkList q = p->next;
	p->next = q->next;
	delete q;
	cout << "单链表删除成功" << endl;
	return OK;
}

Status LinkList_Ergodic(SingleLinkList L) {
	/*
	打印单链表
	创建一个指针p指向首元结点
	当p指针不为NULL时 打印指针p指向的数据域
	p = p->next;指针p后移一个结点
	当指针p指向尾结点时 打印尾结点的数据域后 p = p->next;p指针为NULL 循环结束
	*/
	SingleLinkList p;
	p = L->next;//p指向首元结点
	cout << "**************************" << endl;
	while (p != NULL)
	{
		cout << p->elem << " ";
		p = p->next;
	}
	cout << endl;
	cout << "**************************" << endl;
	return OK;
}

Status LinkList_CreateListForHead(SingleLinkList& L, int n) {
	/*
	头插法创建单链表
	第一步:创建单链表的头结点并将头结点的指针域设置为NULL
		通过new关键字创建一个大小为sizeof(LNode)的内存空间 并将这块空间转换为LNode类型 用L指向新空间的地址
		L = new LNode;
		将头结点的指针域设置为NULL
		L->next = NULL;
	第二步:
		头插法执行一次 需要插入n个结点
		所以需要一个for循环 循环执行以下语句
		通过new创建一个新结点
		LNode* p = new LNode;
		给新结点的数据域赋值
		cin >> p->elem;
		将新结点的指针域设置为NULL
		p->next = L->next;
		将头结点的指针域存储新结点的内存地址
		L->next = p;
	(头插法建立的单链表和输入的值相反)
	*/
	L = new LNode;
	L->next = NULL;
	for (int i = n; i > 0; --i)
	{
		LNode* p = new LNode;
		cout << "请输入头插法要插入的值:" << endl;
		cin >> p->elem;
		p->next = L->next;
		L->next = p;
	}
	cout << "头插法建立单链表成功" << endl;
	return OK;
}

Status LinkList_CreateListForEnd(SingleLinkList& L, int n) {
/*
	尾插法创建单链表
	第一步:
		L = new LNode;
		L->next = NULL;
		通过new关键字创建一个头结点 将头结点的指针域设置为NULL
	第二步:
		头插法执行一次 需要插入n个结点
		所以需要一个for循环 循环执行以下语句
		通过new创建一个新结点
		LNode* p = new LNode;
		给新结点的数据域赋值
		cin >> p->elem;
		p->next = NULL;
		给尾指针指向的结点的next域赋值 赋值的是新结点的地址
		r->next = p;
		尾指针r指向新结点
		r = p;
	(尾插法建立单链表和输入的值相同)
*/
	L = new LNode;
	L->next = NULL;
	SingleLinkList r;
	r = L;
	for (int i = 0; i < n; i++)
	{
		LNode* p = new LNode;
		cout << "请输入尾插法要插入的值:" << endl;
		cin >> p->elem;
		p->next = NULL;
		r->next = p;
		r = p;
	}
	cout << "尾插法建立单链表成功" << endl;
	return OK;
}

Status LinkList_GetElem(SingleLinkList L, int i) {
	/*
	在单链表中查找第i个结点 并输出第i个结点的值
	第一步:
		定义一个指针p指向首元结点 并创建一个计数器变量j
		SingleLinkList p;
		p = L->next;
		int j = 1;
	第二步:
		找到第i个结点 当p指针不为空并且j < i时
		指针p后移一个结点p = p->next 计数器j自增
		if ((p == NULL) && (j > i))检查操作是否成功
		此时p指针指向了第i个结点 输出数据域
	*/
	SingleLinkList p;
	p = L->next;
	int j = 1;
	while ((p != NULL) && (j < i))
	{
		p = p->next;
		++j;
	}
	if ((p == NULL) && (j > i))
	{
		cout << "查找失败" << endl;
		return ERROR;
	}
	ElemType e = p->elem;
	cout << "查找成功" << endl;
	cout << "查找到的数据元素的值为:" << e << endl;
	return OK;
}

int LinkList_LocateElem(SingleLinkList L, ElemType e) {
	/*
	在单链表中查找数据域为e的结点在单链表的位序
	第一步:
		定义一个指针p并指向首元结点 定义一个计数器j = 1
		SingleLinkList p;
		p = L->next;
		int j = 1;
	第二步:
		当指针p不为空并且指针p指向的结点的数据域不等于e p指针后移一个结点 计数器自增
		while ((p != NULL) && (p->elem != e))
		{
			p = p->next;
			j++;
		}		
		在这个过程中如果找到了值等于e的结点 直接返回计数器j的值 j的值就是相应的位序
	*/
	SingleLinkList p;
	p = L->next;
	int j = 1;
	while ((p != NULL) && (p->elem != e))
	{
		p = p->next;
		j++;
	}
	if (p != NULL)
	{
		cout << "查找成功" << endl;
		return j;
	}
	else {
		cout << "查找失败" << endl;
		return 0;
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qq_45671732

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

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

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

打赏作者

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

抵扣说明:

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

余额充值