简述:本篇前面初步陈述链表概念 和 链表与顺序表的一个简单对比,后面的才是重头戏,文章第三部分对单链表的各种操作如增加 删除 查找...本文主要讲述单向链表其他类型会陆续产出 敬请期待。
目录
链表的概念:
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
链表和顺序表对比:
单向链表各接口实现:
这里我们通过创建函数接口 来进行对单链表的各种操作
创建链表类型:
typedef struct ListNode
{
int data;
struct ListNode* next;
}SLTNode;
打印:
为方便后续检测 我们首先实现打印的功能
这里我们的思路就是假设有一个指针 ,从首个节点开始移动 直到移动到NULL为止 打印中途历经的值
void SListPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d ", cur->data);
cur = cur->next;
}
}
(这里不对节点中的数据进行修改 因此只传值操作就可以,区别下列操作)
尾插:
这里就要进行对节点进行实际修改了,这里我们要插入新的值 首先要创建一个节点
因为下面头插也涉及创建节点 我们将其包装成一个函数BuyListNode
SLTNode* BuyListNode(SLTDateType val)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
assert(newnode);
newnode->next = NULL;
newnode->data = val;
return newnode;
}
这里我们需要考虑的是要把原本属于末尾的指针 指向新节点
值得注意的是可能原本没有节点 所以这里我们要分两种情况解决
void SListPushBack(SLTNode** phead, SLTDateType val)
{
SLTNode* newnode = BuyListNode(val);
//无后续链表
SLTNode* tail = *phead;
if (*phead == NULL)
{
*phead = newnode;
}
else
{
while (tail->next!=NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
头插:
头插相对来说比较简单 就是先将新节点的指针指向原本的*phead,再将*phead指向新节点
void SListPushFront(SLTNode** phead, SLTDateType val)
{
SLTNode* newnode = BuyListNode(val);
newnode->next = *phead;
*phead = newnode;
}
尾删:
这里删除节点,很容易就想到通过free实现 但是还要考虑将删除节点之前的指针改为指向null
这里需要注意的是如果没有节点 就不能实现 这里我们可以通过assert来判定
再有就是一个节点 和 大于一个节点在实现上 是有区别的 如果不注意很容易就出现解引用野指针的情况 需要尤其注意
void SListPopBack(SLTNode** phead)
{
assert(*phead != NULL);
SLTNode* tail = *phead;
SLTNode* prev = NULL;
//只有一个
if (tail->next==NULL)
{
*phead = NULL;
free(tail);
tail = NULL;
}
//多于一个
else
{
while (tail->next)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;
}
}
头删:
同样是删除 头删更为简单 不需要考虑一个与多个的区别
只需要将*phead指向之后的一个节点 然后将首个节点释放了即可
void SListPopFront(SLTNode** phead)
{
assert(*phead);
SLTNode* next = (*phead)->next;
free(*phead);
*phead = next;
}
查找:
这里可以参考打印的思路 与之不同的是这里查找到想找的数之后可以直接返回 如果遍历之后找不到可以返回NULL
同样也不必对节点进行修改 因此只进行传值操作即可
SLTNode* SListFind(SLTNode* phead, SLTDateType val)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == val)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
指定位置之前插入:
这里的函数接口需要配合查找函数使用
因为也要涉及插入新节点 需要先创建新节点 然后接收
之后利用创建的指针 到指定的位置 需要做的也是要改变指定节点前一个节点的指针指向
不过这里有个特殊情况 如果指向的节点是第一个节点 如果还用前后指针的方法 容易解引用野指针
就相当于头插。
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDateType val)
{
SLTNode* newnode = BuyListNode(val);
//只有一个单元
//头插
if (*phead==pos)
{
newnode->next = *phead;
*phead = newnode;
}
//大于一个单元
else
{
SLTNode* cur = *phead;
SLTNode* prev = NULL;
while (cur != pos)
{
prev = cur;
cur = cur->next;
}
prev->next = newnode;
newnode->next = cur;
}
}