赞
踩
在上篇顺序表文章中,我们总结出了顺序表的一些缺点。
1.空间不够了需要增容,扩容有代价
2.为了减少扩容的代价,一般扩容扩大两倍,这样可能会导致浪费一定的空间
3.顺序表要求从头位置开始存储,如果我们从头部或者中间开始插入或者删除数据就要挪动数据,效率低
针对顺序表的缺陷,可以设计链表。
本文详细介绍简单的单链表的实现和多种接口函数
每个链表结点有两部分,第一部分存储这个结点的值。第二部分存储链表要连接的下一个结点的地址。
- typedef struct SListNode
- {
- SLDataType val;
- struct SListNode* next;
- }SL;
- SL* CreatListNode(SLDataType x)
- {
- SL* newnode = (SL*)malloc(sizeof(SL));
- if (newnode == NULL)
- {
- printf("malloc失败");
- exit(-1);
- }
- newnode->val = x;
- newnode->next = NULL;
- return newnode;
- }
每次要增加新的结点的时候调用这个函数即可
为了便于观察接口函数的功能 ,我们需要打印链表来观察是否出现错误
- //打印链表
- SL* PrintList(SL* phead)
- {
- SL* cur = phead;
- assert(phead != NULL);
- while (cur != NULL)
- {
- printf("%d->", cur->val);
- cur = cur->next;
- }
- printf("NULL\n");
- }
- //尾插数据
- //注意要使用二级指针,使用一级指针只是传值,不会改变实际参数的值
- void SlistPushback(SL** phead, SLDataType x)
- {
- //1.创建一个新的结点
- SL* newnode = CreatListNode(x);
- //2.将新的结点连接到链表的尾部
- if (*phead == NULL)
- *phead = newnode;
- else
- {
- SL* cur = *phead;
- while (cur->next != NULL)
- {
- cur = cur->next;
- }
- cur->next = newnode;
- }
- }
有一点要注意,由于我们没有哨兵结点,要插入数据需要传入结点的地址,故要使用二级指针。传入一级指针是传值。
- //头插数据
- void SlistPushFront(SL** pphead, SLDataType x)
- {
- //1.创建一个新的结点
- SL* newnode = CreatListNode(x);
- //2.头插数据
- newnode->next = *pphead;
- *pphead = newnode;
- }
头插结点比较简单,无论链表是否为空,让新的结点指向这个链表的头节点即可
然后让这个结点成为新的头
- //尾删数据
- void SlistPopback(SL** pphead)
- {
- assert(*pphead != NULL);
- //1.链表中只有一个结点
- if ((*pphead)->next == NULL)
- {
- free(*pphead);
- *pphead = NULL;
- }
- else//有一个以上的结点
- {
- //1.找到尾节点并且删除这个结点
- SL* tail = *pphead;
- SL* prev = NULL;//用来记录当前尾结点的前一个结点,删除尾结点后让这个结点指向空
- while (tail->next != NULL)
- {
- prev = tail;
- tail = tail->next;
-
- }
- free(tail);
- tail = NULL;
- //倒数第二个结点指向空
- prev->next = NULL;
- }
- }
尾删除结点要注意结点空的个数有空,一个结点,多个结点三种情况需要处理
- //头删数据
- void SlistPopFront(SL** pphead)
- {
- //1.不能为空
- assert(*pphead != NULL);
-
- //2.删除头节点
- SL* next = (*pphead)->next;
- free(*pphead);
- *pphead = next;
- }
注意删除头节点前要先让记录第二个结点,删除头节点后让其称为新的头节点
- //查找数据,查找的时候也能同时修改这个结点
- SL* SlistFind(SL** pphead, SLDataType x)
- {
- assert(*pphead != NULL);
-
- SL* cur = *pphead;
- while (cur)
- {
- if (cur->val == x)
- return cur;
- else
- cur = cur->next;
- }
- return NULL;
- }
返回这个结点,通过返回值还能修改这个结点的值
- //在pos位置之前插入数据
- void SlistInsert(SL** pphead, SL* pos, SLDataType x)
- {
- //1.创建一个新的结点
- SL* newnode = CreatListNode(x);
- //2.找到pos位置并在其前面插入数据
- if (pos == *pphead)//头插
- {
- SlistPushFront(pphead, x);
- }
- else
- {
- SL* posprev = *pphead;
- while (posprev->next != pos)
- {
- posprev = posprev->next;
- }
- posprev->next = newnode;
- newnode->next = pos;
- }
- }
当pos位置为头节点直接调用头插结点即可
- //在pos位置之前插入数据
- void SlistInsertAfter(SL** phead, SL* pos, SLDataType x)
- {
- //1.创建一个新的结点
- SL* newnode = CreatListNode(x);
- //2.插入结点
- newnode->next = pos->next;
- pos->next = newnode;
- }
pos后面插入比较简单
- //删除pos位置的结点
- void SlistEarse(SL** pphead, SL* pos)
- {
- assert(*pphead != NULL);
- if (pos == *pphead)//头删
- {
- SlistPopFront(pphead);
- }
- else
- {
- //找到pos位置的前一个结点
- SL* posprev = *pphead;
- while (posprev->next != pos)
- {
- posprev = posprev->next;
- }
- posprev->next = pos->next;
- free(pos);
- }
- }
-
- //删除pos位置后面的结点
- void SlistEraseAfter(SL* pos)
- {
- assert(pos != NULL);
- SL* next = pos->next;
- pos->next = next->next;
- free(next);
- next = NULL;
- }
- //销毁整个链表
- void SlistDestroy(SL** pphead)
- {
- SL* cur = *pphead;
- while (cur)
- {
- SL* next = cur->next;
- free(cur);
- cur == next;
- }
- free(*pphead);
- *pphead = NULL;
- }
1.按需分配空间,要多少给空间就分配多少空间(分配空间合理)
2.插入,删除数据不需要挪动数据,只需修改少数指针
3.不存在空间的浪费
1.不支持随机访问
2.每个数据都需要一个额外的数据来存储
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。