这里是图片
前言
链表的概念及结构:
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
链表的分类:
链表的分类有很多种:
- 单向或者双向
- 带头或者不带头
- 循环或者非循环
说明:
头是不参与数据的存储,仅存储下一个位置的地址
单向/双向:指的是结构体里面所含指针的个数
循环:指的是最后一个节点的位置存的是首个节点的地址,这样达成了循环的效果
链表的数据在内存中一般不是连续的物理结构,但在逻辑上是连续的,能够很好的达到存数据的效果
虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:
- 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。
- 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。
我们今天要了解的链表为:非循环的单向链表
创建结点
typedef int SLTDataType;//方便之后进行类型修改
typedef struct SListNode
{
SLTDataType data; //存储的数据
struct SListNode* next;//下一个结点的指针
}SLTNode;
单链表的打印
void SLTPrint(SLTNode* phead)
{
//链表不需要断言
//因为如果链表为空phead就为NULL
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d ", cur->data);
cur = cur->next;//找到下一个结点的地址
}
printf("NULL\n");
}
单链表的尾插
如果链表中没有元素,则需要改变首结点指向的内容,所以需要二级指针。
如果不为空,则需要找到尾节点,将尾节点的下一个位置的地址置为新节点的地址。
//单链表尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->data = x;
newnode->next = NULL;
//链表中无数据
//需改变指向内容,用二级指针
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
//后面需要改变的是结构体中的成员,则不需要二级指针
//找到链表的结尾(链表中有数据)
//找尾
SLTNode* tail = *pphead;
注意不能写成这样:
//while (tail != NULL)
//{
// tail = tail->next;
//}
//
//tail = newnode;
while (tail->next != NULL)
{
tail = tail->next;
}
//尾插的本质(链表不为空):原尾结点中要存储新的尾结点的地址
tail->next = newnode;
}
}
尾插运行效果:
int main()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPrint(plist);
return 0;
}
单链表的头插
头插逻辑:
封装之前代码进行复用:
SLTNode* BuySLTNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
//单链表头插
//单链表头插
//单链表头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = BuySLTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
头插运行效果:
int main()
{
SLTNode* plist = NULL;
SLTPushFront(&plist, 1);
SLTPushFront(&plist, 2);
SLTPushFront(&plist, 3);
SLTPushFront(&plist, 4);
SLTPrint(plist);
retutn 0;
}
单链表尾删
//单链表尾删
void SLTPopBack(SLTNode** pphead)
{
//检查链表是否为空
assert(pphead);
assert(*pphead);
//1.只有1个节点
//2.多个节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
//找到链表的结尾(链表中有数据)
//prev记录前一个节点的地址
SLTNode* prev = NULL;
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;
}
}
另一种写法:
//单链表尾删
void SLTPopBack(SLTNode** pphead)
{
//检查链表是否为空
assert(*pphead);
//1.只有1个节点
//2.多个节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
//找到链表的结尾(链表中有数据)
SLTNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
尾删运行效果:
int main()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
return 0;
}
如果链表为空时进行尾删则报错
单链表头删
//单链表头删
void SLTPopFront(SLTNode** pphead)
{
//检查链表是否为空
assert(pphead);
assert(*pphead);
SLTNode* first = *pphead;
*pphead = first->next;
free(first);
first = NULL;
}
头删运行效果:
int main()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPrint(plist);
SLTPopFront(&plist);
SLTPrint(plist);
SLTPopFront(&plist);
SLTPrint(plist);
SLTPopFront(&plist);
SLTPrint(plist);
SLTPopFront(&plist);
SLTPrint(plist);
return 0;
}
如果链表为空时进行头插则报错
单链表查找
//单链表查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
assert(phead);
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
pos位置之前插入
//pos之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pos);
assert(pphead);
//如果插入位置为首位置
if (pos == *pphead)
{
SLTPushFront(pphead, x);
}
else
{
//找到pos前一个位置
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuySLTNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
此方法需要结合链表查找使用:
int main()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTNode* ret = SLTFind(plist, 3);
SLTInsert(&plist, ret, 5);
SLTPrint(plist);
return 0;
}
注:单链表不适合使用pos位置之前插入
pos位置删除
//pos位置删除
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
SLTPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
pos位置删除:
int main()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPrint(plist);
SLTPopBack(&plist);
SLTPrint(plist);
SLTNode* ret = SLTFind(plist, 2);
SLTErase(&plist, ret);
ret = NULL;
SLTPrint(plist);
return 0;
}
注:单链表不适合使用pos位置删除
pos后面插入
//pos后面插入
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = BuySLTNode(x);
//注意顺序不能反
newnode->next = pos->next;
pos->next = newnode;
}
pos位置后面删除
//pos位置后面删除
void SListInsertAfter(SLTNode* pos)
{
assert(pos);
assert(pos->next);
//SLTNode* del = pos->next;
//pos->next = pos->next->next;
//free(del);
//del = NULL;
//或另一种写法:
SLTNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
以上就是本篇的全部内容了,希望大家看完能有所收获。