单链表个人见解及注释

#include <iostream>
using namespace std;

/**************单链表**************/

//LinkNode 声明
typedef struct LNode
{
	char data;										//存放元素值
	struct LNode* next;								//指向后继结点
}LinkNode;											//单链表结点类型

//插入、删除结点的思路
/***插入结点***
*	 s为待插入的结点
*    s->next=p->next;					//将p后面的地址给s的后继结点
*	 p->next=s;							//再让p的后继结点能连上s的值
*	 插入结点有头插法和尾插法
* 
***删除结点***
* \_|_/_|_单链表中删除一个结点的时候需要找到其前驱结点_|_\_|_/

*	 删除结点p后一个结点
*	 p->next=p->next->next;				//将p的后继结点指向p后面一个元素的后继结点
										//此时p后面的那个元素就找不到了
										//这样会造成内存的泄露(可重新启动程序并在运行前修改挽回)
* 或
*	 q=p->next;							//q临时保存被删结点
										//p的后继结点(下一位)给q
*	 p->next=q->next;					//从链表中删除结点q
										//q的后继结点相当于原来p的下一位的后继结点
										//将q的后继结点给到p的后继结点
*	 free(q);							//释放结点q的空间
*/

//插入结点:头插法(含步骤说明)
/*插入步骤解释
* 1、将数组a中的元素生成一个新的结点(由s指向它)
* 2、将读取的数组元素存放到该结点的数据域中
* 3、然后将其插入到当前链表的表头上(即头结点之后)
	 直到数组a的所有元素读完为止
*/
void CreateListF(LinkNode*&L,char a[],int n)
{
	LinkNode* s;
	L = (LinkNode*)malloc(sizeof(LinkNode));
	L->next = NULL;									//创建头结点,其next域置为NULL
	for (int i = 0; i < n; i++)						//循环建立数据结点s
	{
		s = (LinkNode*)malloc(sizeof(LinkNode));
		s->data = a[i];								//建立数据结点s,用于转载数组a[]中的元素到链表中
		s->next = L->next;							//将结点s插入到原首结点之前、头结点之后
		L->next = s;
	}
}

//插入节点:尾插法(含步骤说明)
/*插入步骤解释
* 依次读取数组a中元素放入循环生成的新节点s
* 将其插入当前链表的表尾
* 每次插进去都要重新将尾结点改名字为r
* 如此以来r一直都是尾结点
* 最后将尾结点“r后继节点next”的域置为NULL
*/
void CreateListR(LinkNode*& L, char a[], int n)
{
	LinkNode* s, * r;
	L = (LinkNode*)malloc(sizeof(LinkNode));		//创建头结点
	r = L;											//r始终指向尾结点,初始时指向头结点
	for (int i = 0; i < n; i++)						//循环建立数据结点
	{
		s = (LinkNode*)malloc(sizeof(LinkNode));	//循环建立数据结点s,用于转载数组a中元素到链表
		s->data = a[i];								//将结点s插入到结点r之后
		r->next = s;								//将带插入数据结点s连上尾结点的后继节点	
		r = s;										//再将连上的数据结点s换个r的名字
													//所以r一直都在最后一位,一直都是尾结点
	}
	r->next = NULL;									//尾结点的next域置为NULL
}

//初始化线性表(含步骤说明)
/*步骤解释
* 建立一个空的单链表
* 即创建一个头结点并将其next域置为空(NULL)
*/
void InitList(LinkNode*& L)
{
	L = (LinkNode*)malloc(sizeof(LinkNode));
	L->next = NULL;									//创建头结点,其next域置为NULL
}

//销毁线性表(含步骤说明)
/*步骤解释
* 逐一释放全部结点的空间
* 让pre、p指向两个相邻的结点
* (初始时pre指向头结点,p指向首结点)
		 头结点:最前面的用于引路的结点
		 首结点:第一个有内容的结点
		 关系:头结点是首结点的前驱结点(不知道这样说对不对)
* 当p不是空时产生循环:
*	释放结点pre,pre、p同步后移一个结点
		释放pre,p(pre后面的那个)改名为pre
		再将新pre后面的那个改名为p
		如此便可继续重复循环了
* 循环结束后pre就指向尾结点了,再将其释放
* 
*/
void DestroyList(LinkNode*& L)
{
	LinkNode* pre = L, * p = L->next;				//pre指向结点p的前驱结点
	while (p != NULL)								//扫描单链表L
	{
		free(pre);									//释放pre结点
		pre = p;									//pre、p同步后移一个结点
													//将p(原pre后面的那个)改名为pre
		p = pre->next;								//再将新pre后面的那个改名为p
	}
	free(pre);										//循环结束时p为NULL,pre指向尾结点,释放它
}

//判断线性表是否为空表
bool ListEmpty(LinkNode* L) 
/*未采用应用字符“&”解释
* 此处未采用引用字符“&”
* 因为不需要对链表内部进行改变
*/
{
	//该运算在单链表L中没有数据结点时返回真,否则返回假
	return(L->next == NULL);
}

//求线性表的长度(含步骤说明)
/*
* 让p指向头结点
* n用来累计数据结点个数(初始值为0)
* 遍历
* 不为空时循环:
	n增1
	p指向下一个结点
*/
int ListLength(LinkNode* L)
{
	int n = 0;
	LinkNode* p = L;								//p指向头结点,n置为0(即头结点开始的序号为0)
	while (p->next != NULL)
	{
		n++;
		p = p->next;								//p变成原来p的下一位
	}
	return (n);										//循环结束,p指向尾结点,其序号n为结点个数
}

//输出线性表(含步骤说明)
/*
* 逐一扫描单链表L的每个数据结点
* 不为NULL时输出
*/
void DispList(LinkNode* L)
{
	LinkNode* p = L->next;							//p指向首结点
	while (p != NULL)								//p不为NULL时,输出p结点的data域
	{
		cout << p->data << '\t';
		p = p->next;								//p移向下一个结点
	}
	cout << endl;
}

//求线性表中的某一个数据元素值(含步骤说明)
/*步骤说明
* 在单链表L中从头开始找到第i个结点
* 若存在则将其data域赋值给变量e
* 若没找到i则返回false 
*/
bool GetElem(LinkNode* L, int i, char& e)
{
	int j = 0;
	LinkNode* p = L;								//p指向头结点,j置为0(即头结点的序号为0)
	if (i < 0)
		return false;								//i错误返回false
	while (j < i && p != NULL)						//找第i个结点p
	{
		j++;
		p = p->next;
	}
	if (p == NULL)									//不存在第i个数据结点,返回false
		return false;
	else											//存在第i个数据结点,返回true
	{
		e = p->data;
		return true;
	}
}

//按元素值查找
/*从头开始找第一个值域与e相等的结点,存在则返回逻辑序号,否则返回0*/
int LocateElem(LinkNode* L, char e)
{
	int i = 1;
	LinkNode* p = L->next;							//p指向首结点,i置为1(即首结点的序号为1)
	while (p != NULL && p->data != e)				//查找data值为e的结点,其序号为i
	{
		p = p->next;
		i++;
	}
	if (p == NULL)									//不存在值为e的结点,返回0
		return (0);
	else											//存在值为e的结点,返回其逻辑序号i
		return(i);
}

//插入数据元素e(含步骤说明)
/*步骤说明
* 在单链表上找到第i-1个结点,由p指向它
* 若存在这样的结点,将值为e的结点(s指向它)插入到p所指的结点后面
*/
bool ListInsert(LinkNode*& L, int i, char e)
{
	int j = 0;
	LinkNode* p = L, * s;							//p指向头结点,j置为0(即头结点的序号为0)
	if (i <= 0)										//i错误,返回false
		return false;
	while (j < i - 1 && p != NULL)					//查找i-1个结点p
	{
		j++;
		p = p->next;
	}
	if (p == NULL)									//未找到第i-1个结点,返回false
		return false;
	else											//找到第i-1个结点p,插入新结点并返回true
	{
		s = (LinkNode*)malloc(sizeof(LinkNode));	//创建新结点s
		s->data = e;								//新结点s的data域置为e
		s->next = p->next;							//将结点s插入结点p之后,p的下一位让s的后继结点指向
		p->next = s;								//让p的后继结点指向s
		return true;								//返回true
	}
}

//删除第i个数据元素(含步骤说明)
/*步骤说明
* 书上:
* 先从单链表L中找到第i个结点的前驱结点,即第i-1个结点,由p指向它
* 若存在这样的结点,且也存在后继节点(由p指向它)
* 则删除q所指的结点,返回true
* 否则返回false,表示参数i错误
* 
* 个人见解:
* 找到第i-1个结点,用p指向第i-1个结点
* 若其后继节点,即第i个结点为空,返回false
* 用e承接第i个结点的data域值
* q的后继节点让p的后继节点指向
	即第i个结点的后继结点让第i-1个结点的后继结点指向
	由此便把第i个结点空出来了
* 然后再将空出来的结点free释放掉
*/
bool ListDelete(LinkNode*& L, int i, char e)
{
	int j = 0;
	LinkNode* p = L, * q;							//p指向头结点,j置为0(即头结点的序号为0)
	if (i <= 0)										//i错误,返回false
		return false;
	while(j < i - 1 && p != NULL)					//查找第i-1个结点
	{
		j++;
		p = p->next;
	}
	if (p == NULL)									//未找到第i-1个结点,返回false
		return false;
	else											//找到第i-1个结点p
	{
		q = p->next;								//q指向p后面的那个结点,即第i个结点
		if (q == NULL)								//若不存在第i个结点,返回false
			return false;
		e = q->data;
		p->next = q->next;							//从单链表中删除q结点
		free(q);									//释放q结点
		return true;								//返回true表示成功删除第i个结点
	}
}

void main()
{

}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zhk___

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

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

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

打赏作者

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

抵扣说明:

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

余额充值