数据结构---顺序表之单链表
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,头结点就是NewNodeif (*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;}//未找到返回NULLreturn 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;
}