#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()
{
}
单链表个人见解及注释
于 2021-03-24 18:36:35 首次发布