目录
- 一.链表的介绍
- 二.链表的各种方法
- 单链表的结构
- 初始化链表
- 为链表开辟新节点
- 打印链表
- 尾插
- 头插
- 尾删
- 头删
- 查找
- 指定位置之前插入
- 指定位置之后插入
- 删除(pos)节点
- 删除节点(pos)之后的节点
- 链表的销毁(节点被一个一个地销毁)
一.链表的介绍
链表在物理结构上不一定是连续的
在逻辑结构上一定是连续的(可以通过前一个节点的指针找到下一个节点)
链表是由一个一个的节点组成的,
一个节点存储:指向下一个节点的指针和一个任意类型的数据
由此可以知道链表可以通过前一个节点的指针找到下一个节点,
各个节点就串联起来了
也可以把链表类比成一列火车,火车(链表)有一列列车厢(节点)组成
二.链表的各种方法
链表也是一种顺序结构(是线性表的一种),和顺序表(底层是数组)一样,
有许多的相似的方法
test.c --测试链表方法的文件
SList.h – 声明链表的实现方法和创建链表的结构
SList.c – 实现链表的方法
单链表的结构
typedef int DataType;
//将链表中的数据重命名为DataType,方便下次修改数据类型
typedef struct SListNode
{DataType data;//单链表中的数据struct SListNode* next;//指向下一个节点的指针
}SListNode;
//将链表的名字重命名,方便后续使用
初始化链表
//初始化
//单链表的初始化没有节点
//所以单链表也可以不用初始化
//双向链表的初始化只有一个哨兵位
SListNode* SLInit(SListNode* phead)
{phead = (SListNode*)malloc(sizeof(SListNode));if (phead == NULL){perror("malloc");return NULL;}//申请成功phead->next = NULL;return phead;//返回申请申请成功的节点
}
为链表开辟新节点
//为链表开辟新节点
SListNode* SLByNode(DataType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("malloc");return NULL;}//开辟成功newnode->next = NULL;newnode->data = x;return newnode;//把开辟的新节点返回
}
打印链表
//打印链表
void SLPrint(SListNode* phead)
{SListNode* pcur = phead;while (pcur)// 节点不为空,打印{printf("%d->", pcur->data);//打印当前节点pcur = pcur->next;//指针往下走}printf("NULL\n");//打印一行就换行
}
尾插
//尾插
void SLPushBack(SListNode** pphead,DataType x)
{assert(pphead);// *pphead指向第一个头结点//分为链表为空的尾插和不为空的尾插SListNode* newnode = SLByNode(x);if (*pphead == NULL){*pphead = newnode;//链表为空,头结点就是新节点的尾插}//链表不为空SListNode* pcur = *pphead;while (pcur->next){pcur = pcur->next;}//现在pcur的next指针为NULL, pcur newnode 链接上pcur->next = newnode;newnode->next = NULL;}
头插
//头插
void SLPushFront(SListNode** pphead, DataType x)
{assert(pphead);//链表不为空SListNode* newnode = SLByNode(x);//链接 newnode *ppheadnewnode->next = *pphead;*pphead = newnode;//newnode(*pphead)就是新的头节点
}
尾删
//尾删
void SLPopBack(SListNode** pphead)
{assert(pphead && *pphead);//链表不能为空,节点不能没有SListNode* pcur = *pphead;SListNode* prev = *pphead;if (pcur->next == NULL){//只有一个节点free(*pphead);*pphead = NULL;}else {//有二个及以上节点while (pcur->next!=NULL){prev = pcur;pcur = pcur->next;}//prev指向了最后一个节点的前一个节点//pcur指向了最后一个节点prev->next = NULL;free(pcur);pcur = NULL;}}
头删
//头删
void SLPopFront(SListNode** pphead)
{assert(pphead&&*pphead);//空链表和空节点不能删SListNode* pcur = (*pphead)->next;//->的优先级比*高,所以打括号//先把头节点的下一个节点存起来free(*pphead);//释放头节点*pphead = pcur;//头节点的下一个节点是新的头结点
}
查找
//查找
SListNode* SLCheck(SListNode* phead,DataType x)
{assert(phead);SListNode* pcur = phead;while (pcur){if (pcur->data == x){//找到了return pcur;//返回存储x的节点}pcur = pcur->next;}//没找到return NULL;
}
指定位置之前插入
//在指定位置之前插入数据
void SLInsertF(SListNode** pphead,SListNode* pos,DataType x)
{assert(pphead&&*pphead);//因为有指定位置,链表不能为空SListNode* newnode = SLByNode(x);//为插入的数据开辟一个节点SListNode* pcur = *pphead;while (pcur->next != pos){pcur = pcur->next;}//找到pos之前的数据,在pos前插入数据newnode->next = pcur->next;//右边赋值给左边,pcur->next不会被改变pcur->next = newnode;//pcur还是指向它的下一个节点//这两句不能交换
}
指定位置之后插入
//在指定位置之后插入数据
void SLInsertB(SListNode* pos, DataType x)
{assert(pos);//不能没有节点SListNode* newnode = SLByNode(x);//为新节点申请一个节点newnode->next = pos->next;//右边赋值给左边,pcur->next不会被改变pos->next = newnode;//pcur还是指向它的下一个节点//这两句不能交换
}
删除(pos)节点
//删除pos节点
void SLErase(SListNode** pphead, SListNode* pos)
{assert(pphead && pos);assert(*pphead);//链表为空和没有节点不能删除,指定删除的节点没有,不能删除//可以分为只有一个节点和两个及以上节点两种情况if ((*pphead)->next == NULL){//只有一个节点free(*pphead);*pphead = NULL;}else{//有两个及以上节点//先找到pos节点的前一个节点SListNode* pcur = *pphead;SListNode* plist = pos->next;while (pcur->next != pos){pcur = pcur->next;}//找到pos之前的节点pcur->next = pos->next;// pcur pos pos->next 链接//pcur指向pos的下一个节点,把pos释放free(pos);pos = NULL;}
}
删除节点(pos)之后的节点
等价于删除pos的下一个节点
//删除pos之后的节点
void SLEraseB(SListNode* pos)
{assert(pos);//不能没有节点SListNode* Del = pos->next->next;// pos pcur DelSListNode* pcur = pos->next;//pos的下一个节点pcurpos->next = Del;free(pcur);
}
链表的销毁(节点被一个一个地销毁)
//销毁链表
void SLDestroy(SListNode** pphead)
{//一个节点一个节点地释放assert(*pphead&&pphead);//(没有节点)空链表不用释放,pphead为NULLSListNode* pcur = *pphead;SListNode* prev = *pphead;while (pcur){prev = prev->next;//保存下一个节点free(pcur);pcur = prev;//pcur就指向下一个节点}*pphead = NULL;//临时变量pcur和*pphead指向同一块空间,把临时变量释放掉,还要把*pphead置为NULL//防止野指针*pphead不用的时候
}