目录
1.链表的概念
链表不同于顺序表,顺序表在物理存储结构上连续,而链表是非连续非顺序的,其中间的元素是通过一个个指针所连接起来的。
由此可看出,链表的特点就是在物理上不连续,在逻辑上连续。
2.链表的分类
链表可分为:单向与双向链表,带头与不带头链表。循环与不循环链表。我们最常用的还是两种结构:不带头单向非循环链表,带头双向循环链表。
其中各有各的特点:
不带头单向非循环链表结构简单,多数情况下是以其他数据结构的子结构出现。
带头双向循环链表结构复杂,但是逻辑简单,一般用于单独存放数据。
3.链表的实现(不带头单向非循环链表)
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
void SLTPrint(SLTNode* phead); //打印链表
SLTNode* BuyListNode(SLTDataType x); //创建链表
void SLTPushBack(SLTNode** pphead,SLTDataType x); //尾插
void SLTPushFront(SLTNode** pphead, SLTDataType x); //头插
void SLTPopBack(SLTNode** pphead); //尾删
void SLTPopFront(SLTNode** pphead); //头删
SLTNode* SLTFind(SLTNode* phead, SLTDataType x); //查找
void SLTInsert(SLTNode** pphead,SLTNode* pos, SLTDataType x); //在pos之前插入
void SLTInsertAfter( SLTNode* pos, SLTDataType x); //在pos之后插入
void SLTEraseAfter(SLTNode* pos); //删除pos之后
void SLTDestory(SLTNode** pphead); //销毁链表
3.1打印链表
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL");
}
3.2创建链表
SLTNode* BuyListNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
3.3尾插数据
void SLTPushBack(SLTNode** pphead, SLTDataType x) //尾插
{
SLTNode* newnode = BuyListNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* cur = *pphead;
while (cur->next != NULL)
{
cur = cur->next;
}
cur->next = newnode;
}
}
Tips:传参时不能直接传递插入的链表地址,因为形参为临时变量,如果头指针为空,第一个链表直接拷贝进形参并不能改变实参中的链表,因此需要传二级指针。
3.4头插
void SLTPushFront(SLTNode** pphead, SLTDataType x) //头插
{
SLTNode* newnode = BuyListNode(x);//链表的地址
newnode->next = *pphead;
*pphead = newnode;
}
3.5尾删
void SLTPopBack(SLTNode** pphead) //尾删
{
//1.空
assert(*pphead);
//2.一个节点
//3.多个节点
if ((*pphead)->next == NULL)
{
free(*pphead);
}
else
{
SLTNode* tail = *pphead;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
3.6头删
void SLTPopFront(SLTNode** pphead) //头删
{
//空
assert(*pphead);
//有链表
SLTNode* cur = *pphead; //指向第一个,也就是将删除的
*pphead = (*pphead)->next;
free(cur);
cur = NULL;
}
3.7查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x) //查找
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
cur = cur->next;
}
return NULL;
}
3.8在指定位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) //在pos之前插入
{
assert(pos);
//pos为第一个链表
//pos不为第一个链表
if (*pphead == pos)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* cur = *pphead;
SLTNode* newnode = BuyListNode(x);
while (cur->next) //遍历找到pos的前一个链表
{
if (cur->next == pos) //把前一个链表的next改为新链表
{
cur->next = newnode;
break;
}
cur = cur->next;
}
newnode->next = pos; //把新链表与后面的连接起来
}
}
3.9删除指定位置之后
void SLTEraseAfter(SLTNode* pos) //删除pos之后
{
assert(pos);
assert(pos->next);
SLTNode* cur = pos->next;
pos->next = cur->next;
free(cur);
cur = NULL;
}
3.10销毁链表
void SLTDestory(SLTNode** pphead) //销毁链表
{
SLTNode* newhead = *pphead;
while (newhead)
{
SLTNode* plist = (*pphead)->next;
free(newhead);
newhead = plist;
}
pphead = NULL;
}
4.顺序表与链表的比较
顺序表优点:
1.可以通过下标来随机访问
2.CPU缓存利用率高
缺点:
1.内存不够需要扩展内存,容易造成内存浪费
2.插入删除数据可能需要挪动其他数据
链表优点:
1.不需要扩展内存,不存在浪费问题
2.任意位置插入删除简单
缺点:
1.CPU缓存利用率低
2.随机访问麻烦