单链表
- 链表的概念及结构
- 链表的分类
- 链表的实现
- 初始化
- 打印
- 申请结点
- 头插
- 尾插
- 头删
- 尾删
- 查找
- 在pos位置之后插入
- 在pos位置之前插入
- 删除pos位置之后的值
- 删除pos位置的值
- 销毁
链表的概念及结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
链表的分类
单向或双向
带头或不带头
循环或不循环
虽然有这么多的链表结构,但是我们常用的就两种
无头单向不循环链表
带头双向循环链表
链表的实现
实现的是一个无头的单链表
初始化
//链表的初始化
SLTNode* plist = NULL;
打印
//打印
void SListPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
申请结点
//申请结点
SLTNode* BuySlistNode(SLTDatatype x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
头插
//头插
void SListPushFront(SLTNode* phead, SLTDatatype x)
{
SLTNode* newnode = BuySListNode(x);
newnode->next = phead;
phead = newnode;
}
根据前面的代码,我们头插结点,并打印试试看看代码对不对?
打印NULL,难道是我们代码写错了吗?
考虑这样一个问题,头插的时候,我们需要改变头指针的地址,那就调试一下看看,地址到底改变了吗
看到这里发现了问题,形参的改变并不会影响实参。
举一个相似但很简单的例子,比如交换x,y的值
这里也是形参的改变并不会影响实参,那我们改如何改变x,y的值呢?答案是传址调用
改变int类型我们传地址,如果改变int* 我们该传什么呢?答案是传int* 的地址
所以我们得出这样的结论,改变结构体就传结构体的地址,改变结构体指针就传结构体指针的到地址。
修改一下头插的代码。
//头插
void SListPushFront(SLTNode** pphead, SLTDatatype x)
{
assert(pphead);
SLTNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
尾插
//尾插
void SListPushBack(SLTNode** pphead, SLTDatatype x)
{
assert(pphead);
SLTNode* newnode = BuySListNode(x);
//链表为空
//链表不为空
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
看到这里有些人或许又有点不解,为什么这里cur不用二级指针?
其原因是因为cur访问的结构体里面的内容,并没有要改变指针。
这和上面的一定要做区分
头删
//头删
void SListPopFront(SLTNode** pphead)
{
assert(pphead);
//检查链表是否为空
assert(*pphead);
SLTNode* del = *pphead;
*pphead = (*pphead)->next;
free(del);
del = NULL;
}
尾删
//尾删
void SListPopBack(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);
//方法1
//if ((*pphead)->next == NULL)
//{
// free(*pphead);
// *pphead = NULL;
//}
//else
//{
// SLTNode* del = *pphead;
// SLTNode* prev = NULL;
// while (del->next != NULL)
// {
// prev = del;
// del = del->next;
// }
// free(del);
// del = NULL;
// prev->next = NULL;
//}
//法2
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* del = *pphead;
while (del->next->next != NULL)
{
del = del->next;
}
free(del->next);
del->next = NULL;
}
}
查找
//查找
SLTNode* SListFind(SLTNode* phead, SLTDatatype x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
在pos位置之后插入
//在pos位置之后插入
void SListInsertAfter(SLTNode* pos, SLTDatatype x)
{
assert(pos);
SLTNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
在pos位置之前插入
//在pos位置之前插入
void SListInsertBefor(SLTNode** pphead, SLTNode* pos, SLTDatatype x)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SListPushFront(pphead, x);
}
else
{
SLTNode* newnode = BuySListNode(x);
//寻找pos之前位置
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
newnode->next = pos;
prev->next = newnode;
}
}
删除pos位置之后的值
//删除pos位置之后的值
void SListEraseAfter(SLTNode* pos)
{
assert(pos);
if (pos->next == NULL)
{
return;
}
else
{
SLTNode* del = pos->next;
pos->next = del->next;
free(del);
}
}
删除pos位置的值
//删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SListPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
销毁
//销毁
void SListDestory(SLTNode** pphead)
{
assert(pphead);
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}