链表的简单介绍
链表可根据单向双向、有头无头、循环以及非循环来分类,组合起来一共有8种链表结构。其中最常用的是无头单向非循环链表以及带头双向循环链表。今天实现的是无头单向非循环链表,它的特点是结构简单,一般不会单独用来存数据,实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等。在笔试面试中也出现的很多。而带头双向循环链表的结构最复杂,一般用在单独存储数据。
链表的实现
接口如下:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDataType;
typedef struct SLTNode
{
SLTDataType data;
struct SLTNode* next;
}SLTNode;
SLTNode* BuySLTNode(SLTDataType x) //动态申请一个结点
SLTNode* CreateSLT(int n) //创建一个链表
void SLTPrint(SLTNode* phead) //单链表打印
void SLTPushBack(SLTNode** pphead, SLTDataType x) //单链表尾插
void SLTPopBack(SLTNode** pphead) //单链表尾删
void SLTPushFront(SLTNode** pphead, SLTDataType x) //单链表头插
void SLTPopFront(SLTNode** pphead) //单链表头删
动态申请一个结点
注意只能用malloc而不是直接定义,因为malloc申请的内存是在堆区上的,才不会出现出了函数就销毁了的情况。
SLTNode* BuySLTNode(SLTDataType x)
{
SLTNode* tmp = (SLTNode*)malloc(sizeof(SLTNode));
if (tmp == NULL)
{
perror("malooc fail");
exit(-1);
}
else
{
tmp->data = x;
tmp->next = NULL;
}
return tmp;
}
创建一个单链表
SLTNode* CreateSLT(int n)
{
SLTNode* phead = NULL;
SLTNode* ptail = NULL;
for (int i = 0;i < n;i++)
{
SLTNode* newnode = BuySLTNode(i);//为了方便就设置为i
if (phead == NULL)
{
ptail = phead = newnode;
}
else
{
ptail->next = newnode;
ptail = newnode;
}
}
return phead;
}
打印单链表
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
单链表尾插
注意传参时使用的是二级指针。原因是形参的改变不能影响实参,因此如果我们要改变实参的值应该通过解引用指向他的指针来改变。而在这里,我们需要改变的实参是一个一级指针,因此需要使用二级指针。
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySLTNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
//找尾
while (tail->next)
{
tail = tail->next;
}
tail->next = newnode;
}
}
单链表的尾删
注意:删除时链表不能为空因此需要断言,而插入时链表可以为空。
删除时有两步,将最后一个结点free掉并且将倒数第二个结点的next置为NULL。有两种办法,第一种是创建一个变量记录倒数第二个结点的地址,保障在free掉最后一个结点后仍能够找到倒数第二个结点并将它的next置为NULL;第二种办法如下:
void SLTPopBack(SLTNode** pphead)
{
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
}
else
{
SLTNode* cur = *pphead;
while (cur->next->next != NULL)
{
cur = cur->next;
}
free(cur->next);
cur->next = NULL;
}
}
单链表的头插
头插头删相对来说比尾插尾删要简单。但同样要注意使用的是二级指针。
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySLTNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
newnode->next = *pphead;
*pphead = newnode;
}
}
单链表的头删
void SLTPopFront(SLTNode** pphead)
{
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
}
else
{
SLTNode* tmp = *pphead;
*pphead = (*pphead)->next;
free(tmp);
tmp = NULL;
}
}
总的来说,重点掌握单链表的尾插尾删,头插头删就不成问题啦。