前言
为什么存在链表?因为顺序表存在一定问题,需要优化,而链表可以将顺序表的这些问题优化。
顺序表的问题:
- 中间/头部的插入删除,时间复杂度为O(N)
- 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
- 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到
200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
一、单链表的概念及结构
链表概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
单链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
注意:
1.链式结构在逻辑上是连续的,在物理结构上不一定连续,也就是说实际上链表是没有那个箭头的连接的,只是结点的指针域存储着下一个结点的地址(结构体变量的地址),才得以在逻辑上是连续的。
2.结点,即一个结构体变量,一般是在堆上申请,在堆上申请的空间,是操作系统按一定的策略来分配的,两次动态开辟的空间可能连续,也可能不连续。
3.结点包括数据域和指针域,假设在32位系统上,结点的值域为int类型,则一个结点的大小为8个字节(指针变量的大小是4个字节)。
4.最后指向NULL
二、单链表的实现
1.单链表初始化
void TestSList()
{
SLTNode* pList = NULL;
SListPrint(pList);
}
2.单链表打印
void SListPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
3.创建结点
SLTNode* BuyNewnode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
{
perror("mallo fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
4.单链表头插
void SListPushFront(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuyNewnode(x);
newnode->next = *pphead;
*pphead = newnode;
}
头插测试
5.单链表尾插
void SListPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = BuyNewnode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
尾插测试
6.单链表头删
void SListPopFront(SLTNode** pphead)
{
assert(pphead);
assert(*pphead != NULL);
SLTNode* del = *pphead;
*pphead = (*pphead)->next;
free(del);
del = NULL;
}
7.单链表尾删
注意要分为一个结点和多个结点的情况
void SListPopBack(SLTNode** pphead)
{
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* prev = NULL;
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
prev->next = NULL;
free(tail);
tail = NULL;
}
}
8.单链表销毁
注意有可能有结点,有可能没有结点
void SListDestory(SLTNode** pphead)
{
assert(pphead);
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
9.单链表查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
测试结果
10.单链表插入
注意一定要先用单链表查找函数找到位置为pos的结点 特殊情况:要插入位置的是第一个结点 在pos之前插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SListPopFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
assert(prev);
}
SLTNode* newnode = BuyNewnode(x);
prev->next = newnode;
newnode->next = pos;
}
}
在pos之后插入
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = BuyNewnode(x);
newnode->next = pos->next;
pos->next = newnode;
}
11.单链表删除
void SListDele(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
SListPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
assert(prev);
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
删除pos后面的位置
void SListDeleAfter(SLTNode* pos)
{
assert(pos);
if (pos->next == NULL)
{
return;
}
else
{
SLTNode* next = pos->next;
pos->next = next->next;
free(next);
}
}
|