数据结构–单链表的插入&删除
目标
单链表的插入(位插、前插、后插)
单链表的删除
单链表的插入
按为序插入(带头结点)
ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。
思路:找到第i-1个结点,将新结点插入其后
![](https://i-blog.csdnimg.cn/blog_migrate/5e366fd5568c73605d4731f4d34f88dd.png)
代码实现
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
bool ListInsert(LinkList &L, int i, ElemType e)
{
if (i < 1) return false;
LNode *p = L; //L指向头结点,头结点是第0个结点(不存数据)
int j = 0; //当前p指向的是第几个结点
while (p != NULL && j < i - 1) //循环找到第i-1个结点
{
p = p->next;
j++;
}
if (p == NULL) return false;
LNode* s = (LNode*)malloc(sizeof(LNode));
s->next = p->next;
s->data = e;
p->next = s;
return true;
}
时间复杂度
最好时间复杂度 O(1)
最坏时间复杂度 O(1)
平均时间复杂度 O(1)
按位序插入(不带头结点)
思路:找到第i-1个结点,将新结点插入其后
![](https://i-blog.csdnimg.cn/blog_migrate/c3985db96895d0a565d74d7878090708.png)
代码实现
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
bool ListInsert(LinkList &L, int i, ElemType e)
{
if (i < 1) return false;
if (i == 1) //插入第1个结点的操作与其他结点操作不同
{
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = L;
L = s;
return true;
}
LNode *p = L; //L指向头结点,头结点是第0个结点(不存数据)
int j = 0; //当前p指向的是第几个结点
while (p != NULL && j < i - 1) //循环找到第i-1个结点
{
p = p->next;
j++;
}
if (p == NULL) return false;
LNode* s = (LNode*)malloc(sizeof(LNode));
s->next = p->next;
s->data = e;
p->next = s;
return true;
}
结论:
不带头写代码更不方便,推荐用带头结点
注意:考试中带头、不带头都有可能考察,注意审题
指定结点的后插操作
![](https://i-blog.csdnimg.cn/blog_migrate/9e747fb9ba8173381392b3479d6ec2c1.png)
代码实现
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
bool InsertNextNode(LNode* p, ElemType e)
{
if (p == NULL) return false;
LNode* s = (LNode*)malloc(sizeof(LNode));
if (s == NULL) return false; // 内存分配失败
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
指定结点的前插操作
前插操作:在p结点之前插入元素e
bool InsertPriorNode (LNode *p,ElemType e)
![](https://i-blog.csdnimg.cn/blog_migrate/2adb360e04a261388cf75f60dfe37599.png)
方法一:
bool InsertPriorNode (LinkListL L, Node *p,ElemType e)
传入头指针,循环查找p的前驱,再对q后插
时间复杂度:O(n)
方法二 \color{red}方法二 方法二
![](https://i-blog.csdnimg.cn/blog_migrate/7af13a3f20ea955792aca8631e437e38.png)
方法二实现代码
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
bool InsertPriorNode (LNode *p,ElemType e)
{
if (p == NULL) return false;
LNode* s = (LNode*)malloc(sizeof(LNode));
if (s == NULL) return false;
s->next = p->next;
p->next = s; //新结点s连到p之后
s->data = p->data; //将p中元素复制到s中
p->data = e; //p 中元素覆盖为e
return true;
}
时间复杂度: O(n)
前插操作:在p结点之前插入结点 s
代码实现
bool InsertPriorNode(LNode* p, LNode* s)
{
if (p == NULL || s == NULL) return false;
s->next = p->next;
p->next = s;
ElemType tmp = p->data;
p->data = s->data;
s->data = tmp;
return true;
}
单链表的删除
按位序删除(带头结点)
ListDelete(&L,i,&e):删除操作。删除表L中第i个位置的元素,并用e返回删除元素的值。
方法:
找到第i-1个结点,将其指针指向第i+1个结点,并释放第i个结点
![](https://i-blog.csdnimg.cn/blog_migrate/cec0da53cf5dbedbe7c421a2bd659709.png)
代码实现
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
bool ListDelete(LinkList &L, int i, ElemType &e)
{
if (i < 1) return false;
LNode *p = L;
int j = 0;
while (p != NULL && j < i - 1)
{
p = p->next;
j++;
}
if (p == NULL) return false;
if (p->next == NULL) return false; //第i-1个结点之后已无其他结点
LNode* q = p->next;
e = q->data; //用e返回元素的值
p->next = q->next; //将*q结点从链中“断开"
free(q); //释放结点的存储空间
return true;
}
时间复杂度:O(n)
删除指定结点p
bool DeleteNode ( LNode *p)
![](https://i-blog.csdnimg.cn/blog_migrate/c7fcf1a98e107a1b3e4fcef08bcd689f.png)
方法1:传入头指针,循环寻找p的前驱结点
时间复杂度O(n)
方法2:偷天换日(类似于结点前插的实现)
时间复杂度O(1)
方法二代码实现
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
bool DeleteNode(LNode* p)
{
if (p == NULL) return false;
LNode* q = p->next; //令q指向*p的后继结点
p->data = p->next->data; //和后继结点交换数据域
p->next = q->next; //将*q结点从链中"断开"
free(q);
return true;
}
注:
\color{red}注:
注:
如果
p
是最后一个结点,只能从表头开始依次寻找
p
的前驱
,
时间复杂度
O
(
n
)
\color{red} 如果p是最后一个结点,只能从表头开始依次寻找p的前驱,时间复杂度O(n)
如果p是最后一个结点,只能从表头开始依次寻找p的前驱,时间复杂度O(n)
![](https://i-blog.csdnimg.cn/blog_migrate/cc758ab7b57076d10df0816141f7faa1.png)
知识点回顾与重要考点
![](https://i-blog.csdnimg.cn/blog_migrate/46ccdea30a0018cb74837feeb63222be.png)