赞
踩
目录
1. 中间/头部的插入删除,时间复杂度为O(N)
2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
链表结构是有一个Data存储值/数据,有一个结构体指针存放下一个结点的地址
1.单向链表或双向链表
2.带头(哨兵位)链表或不带头(哨兵位)链表
3.循环链表或不循环链表
每种类型都有两种不同的结构,所以一共能出8种(2*2*2)不同的结构
虽然链表的种类众多,但是我们主要用两种链表
无头单向非循环链表和带头双向循环链表
1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构
2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势
注:为什么有的函数传递二级指针
因为当链表的头结点需要被改变时,需要传递头结点的地址,以此才能改变头结点
- typedef int DataType;
-
- typedef struct Slist
- {
- DataType data;
- struct Slist* next;
- }SL;
函数声明:
SL* BuyListNode(DataType x);
在VS中malloc一个内存块时,注意检查一下malloc是否成功,最后返回malloc出来的结点地址
函数定义:
- SL* BuyListNode(DataType x)
- {
- SL* newnode = (SL*)malloc(sizeof(SL));
- if (newnode == NULL)
- {
- printf("malloc errno\n");
- exit(-1);
- }
-
- newnode->data = x;
- newnode->next = NULL;
-
- return newnode;
- }
函数声明:
void SlPrint(SL* phead);
注:当while循环中是cur != NULL时,最后cur会指向链表最后一个元素存储的NULL
当while循环中是cur->next !=NULL时,最后cur会指向链表的最后一个元素
函数定义:
- void SlPrint(SL* ps)
- {
- SL* cur = ps;
- //while (cur)
- while (cur != NULL)
- {
- printf("%d->",cur->data);
- cur = cur->next;
- }
-
- printf("NULL\n");
- }
尾插:
函数声明:
void SlPushBack(SL** pphead, DataType x);
无头单向非循环链表尾插需要通过遍历才能找到链表的尾,有些麻烦
尾插时,分两种情况:
1.链表中还没有存储数据,此时链表的头结点是NULL
此时直接将malloc出来的结点给头结点
2.链表中已经有数据,此时链表的头结点不是NULL
此时需要先找到尾结点,然后再把malloc出来的结点给tail->next
函数定义:
- void SlPushBack(SL** pphead, DataType x)
- {
- assert(pphead);
- SL* newnode = BuyListNode(x);
-
- if (*pphead == NULL)//1
- {
- *pphead = newnode;
- }
- else
- {
- //找尾
- SL* tail = *pphead;//2
-
- while (tail->next != NULL)
- {
- tail = tail->next;
- }
-
- tail->next = newnode;
- }
- }

尾删:
函数声明:
void SlPopBack(SL** pphead);
首先需要检查传进函数的指针不为空
尾删时,分两种情况:
1.链表中只有一个结点:此时直接free掉这个结点,然后再让它为空
2.链表中不只有一个结点:
此时需要找到尾结点,同时也需要记录尾结点的前一个结点,最后让前一个节点的指向NULL
(原因:若只找到尾结点并释放掉的话,尾结点的前一个结点还指向原来的尾结点地址,如果打印时此时会造成指针的非法访问)
函数定义:
- void SlPopBack(SL** pphead)
- {
- assert(*pphead);
-
- if ((*pphead)->next == NULL)
- {
- free(*pphead);
- *pphead = NULL;
- }
- else
- {
- SL* end = *pphead;
- SL* prve = NULL;
- while (end->next)
- {
- prve = end;
- end = end->next;
- }
-
- free(end);
- end = NULL;
-
- prve->next = NULL;
- }
-
- }

头插:
函数声明:
void SlPushFront(SL** pphead, DataType x);
只需要检查一下指针不为NULL,然后将malloc出来的结点直接插入
函数定义:
- void SlPushFront(SL** pphead, DataType x)
- {
- assert(pphead);
-
- SL* newnode = BuyListNode(x);
- newnode->next = *pphead;
- *pphead = newnode;
- }
头删:
函数声明:
void SlPopFront(SL** pphead);
需要检查一下指针不为NULL,记录第二个结点地址,释放第一个结点,将第二个结点地址给头结点
函数定义:
- void SlPopFront(SL** pphead)
- {
- assert(*pphead != NULL);
- assert(pphead);
-
- SL* next = (*pphead)->next;
- free(*pphead);
-
- *pphead = next;
-
- }
函数声明:
SL* SlFind(SL* phead, DataType x);
遍历链表,找到返回结点地址,找不到返回NULL
函数定义:
- SL* SlFind(SL* phead, DataType x)
- {
- assert(phead);
-
- while (phead)
- {
- if (phead->data == x)
- {
- return phead;
- }
- else
- {
- phead = phead->next;
- }
- }
- return NULL;
- }

在pos前面插入一个数:
函数声明:
void SlInsertBefore(SL** pphead, SL* pos, DataType x);
1.pos是头结点,此时就是头结点的前插
2.在其他位置前插一个数,需要记录pos位置的前一个结点的位置
函数定义:
- void SlInsertBefore(SL** pphead, SL* pos, DataType x)
- {
- assert(*pphead);
- assert(pos);
-
- if (*pphead == pos)
- {
- SlPushFront(pphead,x);
- }
- else
- {
- SL* prev = *pphead;
- while (prev->next != pos)
- {
- prev = (*pphead)->next;
- }
-
- SL* newnode = BuyListNode(x);
- newnode->next = pos;
- prev->next = newnode;
- }
- }

在pos后面插入一个数:
函数声明:
void SlInsertAfter(SL* pos, DataType x);
只直接改变两个结点的next指针即可
函数定义:
- void SlInsertAfter(SL* pos, DataType x)
- {
- SL* newnode = BuyListNode(x);
- newnode->next = pos->next;
- pos->next = newnode;
- }
删除某个位置 :
函数声明:
void SlErase(SL** pphead, SL* pos);
1. pos是头结点时,调用头删函数
2. pos是其他结点,通过遍历找到pos的前一个结点,将pos前一个结点的next指向pos的下一个结点,最后free掉pos
函数定义:
- void SlErase(SL** pphead, SL* pos)
- {
- assert(*pphead);
- assert(pos);
- if(*pphead == pos)
- {
- SlPopFront(pphead);
- }
- else
- {
- SL* cur = *pphead;
- while (cur->next != pos)
- {
- cur = cur->next;
- }
- cur->next = pos->next;
- free(pos);
- pos = NULL;
- }

删除某个位置的后一个位置:
函数声明:
void SListEraseAfter(SL* pos);
记录pos下两个结点的地址
将pos下两个结点的地址给pos->next
函数定义:
- void SListEraseAfter(SL* pos)
- {
- assert(pos);
- assert(pos->next);
-
- SL* next = pos->next;
- pos->next = next->next;
-
- free(next);
- next = NULL;
- }
函数声明:
void SlDestory(SL** pphead);
通过遍历链表释放每个结点
函数定义:
- void SlDestory(SL** pphead)
- {
- assert(pphead);
-
- SL* cur = *pphead;
- while (cur)
- {
- SL* next = cur->next;
- free(cur);
- cur = next;
- }
- *pphead = NULL;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。