一、链表的概念及其结构
链表是一种不同于顺序表的结构,其是一种物理存储结构上非连续、非顺序的存储结构,但是一种逻辑上连续的结构,其数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
如图所示,链式结构在逻辑上是连续的,但是在物理上不一定连续。现实中的结点一般都是从堆上申请出来的,但是从堆上申请出来的空间是按照一定的策略来分配的,两次申请的空间可能是连续的但是也可能是不连续的。
二、链表的分类
链表的结构是多样的,从以下情况中组合起来也就把八种情况。
1、单向或者双向
2、带头或者不带头
3、循环或者非循环
4、常用链表结构
1)无头单项非循环链表
结构简单,但是一般不会用来存储数据,更多是用来作为其他数据结构的子结构。如哈希桶、图的邻接表等。
2)带头的双向循环链表
结构复杂,一般用来单独存储数据,实际中使用的链表的数据结构都是带头双向循环链表。
三、链表的功能实现
1、头文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SListNode;
//打印
void SListPrint(SListNode* phead);
//初始化
void SListInit(SListNode* phead);
SListNode* BuySListNode(SLTDataType x);//申请节点
void SListPushBack(SListNode** pphead, SLTDataType x);//尾插
void SListPopBack(SListNode** pphead);//尾删
void SListPushFront(SListNode** pphead, SLTDataType x);//头插
void SListPopFront(SListNode** pphead);//头删
2、链表的创建和初始化
我们实现的是无头单项非循环链表 ,所以每一个节点除了要保存其数据元素外还要保存其下一个节点的地址以方便找到。所以我们需要一个结构体。
结点的初始化:创建一个链表,其只有一个节点,零其指向的下一个节点为空。在增添数据元素时需要再申请新的节点,直接通过malloc函数开辟一块空间。
SListNode* BuySListNode(SLTDataType x)
{
SListNode* NewNode = (SListNode*)malloc(sizeof(SListNode));
if (NewNode == NULL)
{
printf("申请节点失败\n");
exit(-1);
}
NewNode->data = x;
NewNode->next = NULL;
return NewNode;
}
3、销毁链表
通过循环释放掉每一个节点
//销毁链表
void SListDestory(SListNode** pphead)
{
assert(pphead);
while (*pphead)
{
SListNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
}
4、尾插
先申请一个新的节点,通过遍历找到链表的尾巴,将尾巴的存放的地址改为新申请的节点的地址,再将新申请节点指向空。
void SListPushBack(SListNode** pphead, SLTDataType x)
{
SListNode* NewNode=BuySListNode(x);
if (*pphead==NULL)
{
*pphead = NewNode;
}
else
{//找尾
SListNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = NewNode;
}
}
5、尾删
分三种情况,头节点为空,一个节点和至少两个节点
void SListPopBack(SListNode** pphead)
{
//空
if (*pphead == NULL)
{
return;
}
else if ((*pphead)->next == NULL)//第一个不空
{
free(*pphead);
*pphead == NULL;
}
else//其他不空
{
SListNode* prev = NULL;
SListNode* tail =*pphead;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
prev->next = NULL;
}
}
6、头插
void SListPushFront(SListNode** pphead, SLTDataType x)
{
SListNode* NewNode = BuySListNode(x);
if (*pphead == NULL)
{
*pphead = NewNode;
}
else
{
NewNode->next = *pphead;
*pphead = NewNode;
}
}
7、头删
void SListPopFront(SListNode** pphead)
{
if ((*pphead == NULL))
{
return;
}
else if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead= NULL;
}
else
{
*pphead = (*pphead)->next;
}
}
8、查找元素
SListNode* SListFind(SListNode* phead, SLTDataType x)
{
SListNode* cru = phead;
while (cru)
{
if (cru->data == x)
{
return cru;
}
cru = cru->next;
}
return NULL;
}
9、从pos后插入
void SListInserAfter(SListNode** pos, SLTDataType x)//从pos后插入
{
SListNode* NewNode = BuySListNode(x);
NewNode->next = (*pos)->next;
(*pos)->next = NewNode;
}
10、删除pos后的值
void SListEraseAfter(SListNode* pos)//删除pos后的节点
{
assert(pos);
if (pos->next)
{
SListNode* next = pos->next;
SListNode* nextnext = next->next;
pos->next = nextnext;
free(next);
}
}