上篇博客详细介绍了动态顺序表的实现,而顺序表会存在如下的几个问题:
(1). 中间/头部的插入删除,时间复杂度为O(N);
(2). 增容需要申请新空间,拷贝数据,释放旧空间,带来不少的消耗;
(3). 增容一般呈2倍增长,势必会有一定的空间浪费;
因此,为了解决上述问题,我们引入了链表:
单链表
单链表的逻辑结构如下图所示,我们可以通过定义一个结构体,包含数据和下一块空间的地址:
为此我们首先可以进行如下定义,包含数据和下一级空间的地址:
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
那么我们如何实现对单链表数据的访问呢?代码如下:
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
通过判断 cur 是否为空,来访问每一块空间访问的数据。接下来我们将演示如何在单链表的起始位置插入数据:
void SLPushFront(SLTNode** pphead, SLTDataType x)//头插
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->data = x;
newnode->next = *pphead;
*pphead = newnode;
}
通过 malloc 申请了一块空间,起始地址为 newnode , 关于 newnode->next = *pphead;
*pphead = newnode; 很容易理解,我们在单链表的头部插入数值,那么此时下一级空间地址即为 phead ,接着再将初始申请的地址 newnode 赋给 phead 即可,因为 phead 我们保证其为单链表第一块空间的地址。而为了让两个指针发生改变,此处使用了二级指针,二级指针解引用才能完成交换。示意图如下:
最后我们可以在主函数中验证我们的头部插入:
int main()
{
SLTNode* plist = NULL;
SLPushFront(&plist, 1);
SLPushFront(&plist, 2);
SLPushFront(&plist, 3);
SLPushFront(&plist, 4);
SLTPrint(plist);
return 0;
}