1.什么是链表
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。
首先定义了一个结构体,结构体成员中有结构体指针,我们就是通过这个指针来访问下一个结构体的
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
接着在内存中开辟四块结构体大小的空间,返回类型转换为结构体指针类型,每一个结构体里面的data成员都赋一个值,指针都指向下一个结构体
void TestSLTNode1()
{
SLTNode* n1 = (SLTNode*)malloc(sizeof(SLTNode));
SLTNode* n2 = (SLTNode*)malloc(sizeof(SLTNode));
SLTNode* n3 = (SLTNode*)malloc(sizeof(SLTNode));
SLTNode* n4 = (SLTNode*)malloc(sizeof(SLTNode));
n1->data = 1;
n2->data = 2;
n3->data = 3;
n4->data = 4;
n1->next = n2;
n2->next = n3;
n3->next = n4;
n4->next = NULL;
}
用图表示就是这个样子
通过指针来进行链接下一个节点,这个就是链表,同时最后一个节点的指针要置为空指针
2.链表实现增删查改
首先来实现链表的打印
把传到函数里的指针赋值给临时指针cur,让cur通过将结构体里面的结构体指针赋值给自己去遍历每一个结构体,当cur为空指针的时候就停下来
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
实现链表的尾增
在最后一个节点上再连接上一个节点就好了,创建一个临时的指针变量tail,当tail->next==NULL的时候就让这个指针停止走动
其次要考虑到空指针的情况,所以要传一个结构体的指针的地址进来,才能对实参的值进行改变
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead); //二级指针不能为空
SLTNode* tmp = (SLTNode*)malloc(sizeof(SLTNode));
assert(tmp);
SLTNode* newnode = tmp;
newnode->data = x;
newnode->next = NULL;
if (*pphead == NULL) //一个节点也没有
{
*pphead = newnode;
}
else
{
//找到最后一个节点
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
将增加创建新节点的代码封装成一个函数
SLTNode* BuySLTNode(x)
{
assert(pphead); //二级指针不能为空
SLTNode* tmp = (SLTNode*)malloc(sizeof(SLTNode));
assert(tmp);
SLTNode* newnode = tmp;
newnode->data = x;
newnode->next = NULL;
return newnode;
}
实现链表的头增
让phead连接上新节点,并让新节点中的next指向后一个节点
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead); //二级指针不能为空
SLTNode* newnode = BuySLTNode(x);
if (*pphead == NULL) //一个节点也没有
{
*pphead = newnode;
}
else
{
SLTNode* begin = *pphead; //先保存下来原来第一个节点的地址
*pphead = newnode;
newnode->next = begin;
}
}
实现链表的尾删
让一个cur指针更着tail指针,在最后一个节点删除掉后,可以让倒数第二个节点的next置为NULL
void SLTPopBack(SLTNode** pphead)
{
assert(pphead); //二级指针不能为空
assert(*pphead); //没有节点不用删除
if ((*pphead)->next == NULL) //只有一个节点
{
free(*pphead);
*pphead = NULL;
}
//有多个节点
else
{
SLTNode* tail = *pphead;
SLTNode* cur = NULL;
while (tail->next != NULL)
{
cur = tail;
tail = tail->next;
}
free(tail);
cur->next = NULL;
}
}
实现链表的头删
void SLTPopFront(SLTNode** pphead)
{
assert(pphead); //二级指针不能为空
assert(*pphead); //没有节点不用删除
SLTNode* begin = *pphead;
*pphead = (*pphead)->next;
free(begin);
}
实现链表的查找
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x)
{
assert(pphead); //二级指针不能为空
SLTNode* cur = *pphead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
//找不到返回空指针
return NULL;
}
在pos位置之前插入
如果pos位置是头节点的话,相当于是头插了
如果是中间节点的话,要在pos位置之前插入,就要先找到pos位置的前一个节点,记为prev,
让prev的next指向新节点,新节点的next指向pos
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead);
assert(pos); //pos不能为空,不然就变成尾增了
if (pos == *pphead) //pos为第一个节点,直接复用头增的代码
{
SLTPushFront(pphead, x);
}
else //pos为中间节点
{
SLTNode* newnode = BuySLTNode(x);
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = newnode;
newnode->next = pos;
}
}
删除pos位置的节点
先找到pos的前一个位置的节点,记为prev
然后找到将prev的next链接上pos的下一个节点
再把pos指向的节点给free掉
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (pos == *pphead) //pos为第一个节点,复用头删的代码
{
SLTPopFront(pphead);
}
else //pos为中间节点
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
在pos位置之后插入
先让newnode的next链接上pos位置的下一个节点
然后再把pos位置的next链接上newnode
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = BuySLTNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
在pos位置之后删除
先把pos位置之后的节点记为del
再把pos位置的next链接上del的下一个节点
最后删除del节点
void SLTEraseAfter(SLTNode* pos)
{
assert(pos && pos->next);
SLTNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
总结:学习数据结构需要多画图