目录
9、删除指定位置之后的节点(SListEraseAfter)
前言:
链表的概念:
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的。简单的说,就是一些结构体相互关联起来,而关联的手段就是指针,通过存储下一个结构体的地址,就能挨个访问存在结构体里的数据。
顺序表和链表的优缺点:
顺序表:
顺序表的优点:
- 支持随机访问,有些算法需要结构随机访问,比如二分查找和优化的快排等。
- 数据是按顺序存放的,空间利用率高。
- 通过下标直接访问,存取速度高效。
顺序表的缺陷:
- 空间不够了就需要扩容,扩容是存在消耗的。
- 头部或者中间位置的插入或删除,需要挪动,挪动数据时也是存在消耗的。
- 避免频繁扩容,一次一般都是按倍数去扩容(2倍适中),可能存在一定空间的浪费现象。
链表:
链表的优点:
- 按需申请空间,不用就释放空间( 链表可以更合理地使用空间)。
- 头部或者中间位置的插入和删除,不需要挪动数据。
- 不存在空间浪费。
链表的缺陷:
- 每一个数据,都要存一个指针去链接后面的数据节点。
- 不支持随机访问(用下标直接访问第 i 个),必须得走 O(N) 。
(建议先阅读顺序表,更好理解链表)
链表的结构与定义
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data; // val
struct SListNode* next; // 存储下一个节点的地址
}SListNode, SLN;
解读:在顺序表章节讲过,为了方便后续使用我们将类型 typedef 一下。首先创建结构体,因为叫单链表(SingleListNode),所以我们将它取为 SListNode。结构体有两个变量,data 是用来存放节点的数据的变量,而 next 是用来指向后继节点指针的变量。
链表结构图:
便于更形象方便地理解而想象出来的(比如这个箭头其实不存在),事实上在内存中是分散存储的,其中唯一联系就是指针,若指向某块链表的指针搞丢了,那么这块结构体以及其后接的结构体就再也找不到了
接口函数
void SListPrint(SListNode* phead);
void SListPushBack(SListNode** pphead, SLTDataType x);
void SListPushFront(SListNode** pphead, SLTDataType x);
void SListPopBack(SListNode** pphead);
void SListPopFront(SListNode** pphead);
SListNode* SListFind(SListNode* phead, SLTDataType x);
// 在pos位置之前插入
void SListInsert(SListNode** pphead, SListNode* pos, SLTDataType x);
// 删除pos 位置
void SListErase(SListNode** pphead, SListNode* pos);
// 在pos之后插入
void SListInsertAfter(SListNode* pos, SLTDataType x);
// 删除pos位置后面的值
void SListEraseAfter(SListNode* pos);
void SListDestroy(SListNode** pphead);
详解接口函数的实现
创建新节点(BuySListNode)
SListNode* BuySListNode(SLTDataType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
else
{
newnode->data = x;
newnode->next = NULL;
}
return newnode;
}
考虑到创建新节点要经常用,为了方便复用,我们把它写成函数
打印(SListPrint)
void SListPrint(SListNode* phead)
{
SListNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
我们想实现