1.链表的概念
链表是一种逻辑上是线性的,但物理结构不一定是线性的数据结构,它通过链表中的指针链接次序实现的
链表的存储空间是我们通过动态内存开辟的内存空间,所以他们的地址可能是连续的也可能不是连续的
2.链表的分类
1.单向或者双向
2.带头或者不带头
3.循环或者不循环
虽然链表有这么多种类,单我们常用的就只有两种:
单向不带头不循环链表(单链表)
双向带头循环链表(双链表)
3.单链表的实现
SList.h
// 1、无头+单向+非循环链表增删查改实现
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
// 动态申请一个结点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);
SList.c
//为什么这里要用二级指针?
//因为我们的头结点是一个结构体指针,我们可能会改变头结点,所以我们需要传结构体地址的地址
// 动态申请一个结点
SListNode* BuySListNode(SLTDateType x) {
SListNode* NewNode = (SListNode*)malloc(sizeof(SListNode));
if (NewNode == NULL) {
return 0;
}
NewNode->next = NULL;
NewNode->data = x;
return NewNode;
}
// 单链表打印
void SListPrint(SListNode* plist) {
SListNode* pcur = plist;
while (pcur) {
printf("%d->", pcur->data);
pcur = pcur->next;
}
printf("NULL\n");
}
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x) {
assert(pplist);
SListNode* NewNode = BuySListNode(x);
//如果链表为NULL,头结点就是NewNode
if (*pplist == NULL) {
*pplist = NewNode;
return;
}
//链表不为NULL,找到尾节点插入
SListNode* pcur = *pplist;
while (pcur->next) {
pcur = pcur->next;
}
pcur->next = NewNode;
}
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x) {
assert(pplist);
SListNode* NewNode = BuySListNode(x);
SListNode* pcur = *pplist;
NewNode->next = pcur;
*pplist = NewNode;
}
// 单链表的尾删
void SListPopBack(SListNode** pplist) {
assert(pplist);
//链表为NULL不能执行删除
assert(*pplist);
//如果链表只有1个节点
if ((*pplist)->next == NULL) {
free(*pplist);
*pplist = NULL;
return;
}
SListNode* pcur = *pplist;
SListNode* prev = NULL;
while (pcur->next) {
prev = pcur;
pcur = pcur->next;
}
prev->next = pcur->next;
free(pcur);
pcur = NULL;
}
// 单链表头删
void SListPopFront(SListNode** pplist) {
assert(pplist);
assert(*pplist);
SListNode* pcur = *pplist;
*pplist = pcur->next;
free(pcur);
pcur = NULL;
}
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x) {
assert(plist);
SListNode* pcur = plist;
while (pcur) {
if (pcur->data == x) {
return pcur;
}
pcur = pcur->next;
}
//未找到返回NULL
return NULL;
}
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
//因为单链表的节点只能找后继结点,不能找前驱
void SListInsertAfter(SListNode* pos, SLTDateType x) {
assert(pos);
SListNode* NewNode = BuySListNode(x);
NewNode->next = pos->next;
pos->next = NewNode;
}
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
// 如果链表存在多个节点,pos节点之前的节点就会丢失,造成内存泄露
void SListEraseAfter(SListNode* pos) {
assert(pos);
assert(pos->next);
SListNode* DeleNode = pos->next;
pos->next = DeleNode->next;
free(DeleNode);
DeleNode = NULL;
}