目录
链表的概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
链表的构建
struct SListNode
{
int val;
struct SListNode* next;
};
val用来储存值,next来储存下一个值的地址。
接下来就是经典的链表的增删查改了;
创建链表(动态申请节点)
要创建新的值,避免不了选择,堆上或栈上,堆上的好处是只要不free()就不会销毁,当然可能会由于使用不当造成内存泄漏,栈上是退出当前函数数据就会自动销毁。所以我们使用malloc。
我们创建结构体时创建了一个链表的值和下一个链表地址的指针。
因为链表是通过一个个指针所指向的地址才能找到他们的值的。他们在堆中不是连续的。
//动态申请节点
SLTNode* BuySLTNode(SLTDateType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror(BuySLTNode);
exit(0);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
//创建链表
SLTNode* CreateSLT(int n)
{
SLTNode* phead = NULL,*ptail = NULL;
for (int i = 0; i < n; i++)
{
SLTNode* newnode = BuySLTNode(i);
if (phead == NULL)
{
phead = ptail = newnode;
}
else
{
ptail->next = newnode;
ptail = newnode;
}
}
return phead;
}
链表的打印
链表打印是从头结点开始,一个一个遍历下一个节点。
当链表的下一个节点为NULL时打印结束。
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d-> ", cur->data);
cur = cur->next;
}
printf("NULL");
}
头插尾插
接下来就是链表的增删查改了。
和上期顺序表的思路稍微不同的是:链表增删查改时,比如尾插必须遍历到尾部然后插入。
需要考虑的是链表为空将其新节点设为头结点。
头插则是将头结点改为新节点
// 单链表尾插
void SLTPushBack(SLTNode** pplist, SLTDateType x)
{
SLTNode* newnode = BuySLTNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
SLTNode* tail = *pplist;
while (tail->next)
{
tail = tail->next;
}
tail->next = newnode;
}
}
// 单链表的头插
void SListPushFront(SLTNode** pplist, SLTDateType x)
{
SLTNode* newnode = BuySLTNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
尾删头删
删除时我们需要找到要删除值的前一个值,需要新创建一个指针指向我们所遍历节点的头一个节点。
为什么使用二级指针呢?
这个地方使用二级指针的目的时:我们需要改变链表中的值,需要链表的地址,才能改变举个例子
你改变函数的形参,他出了这个函数是就会销毁,形参并不会改变实参的值。
所以我们有两种方法,一种是前面提到的二级指针,还有一种是返回形参的地址。
// 单链表的尾删 int** p *p int*
void SListPopBack(SLTNode** pplist)
{
assert(*pplist);
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SLTNode* prev = NULL;
SLTNode* tail = *pplist;
while (tail->next)
{
prev = tail;
tail = tail->next;
}
free(tail);
prev->next = NULL;
}
}
// 单链表头删
void SListPopFront(SLTNode** pplist)
{
assert(pplist);
SLTNode* next = (*pplist)->next;
free(*pplist);
*pplist = next;
}
查找
查找需要一个一个遍历,找到该值时返回其地址。
SLTNode* SListFind(SLTNode* plist, SLTDateType x)
{
SLTNode* cul = plist;
while (cul!= NULL)
{
if (cul->data == x)
{
return cul;
}
cul = cul->next;
}
return NULL;
}
在pos位置插入
在pos位置和查找有关联,查找到pos位置后,找到该位置后,再找到它前面的节点,然后插入新节点。删除和上面差不多
// 单链表在pos位置之后插入x
void SListInsertAfter(SLTNode* pos, SLTDateType x)
{
assert(pos);
SLTNode* newnode = BuySLTNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
//在pos之前插入
void SListInsert(SLTNode** plist,SLTNode* pos, SLTDateType x)
{
assert(pos);
SLTNode* newnode = BuySLTNode(x);
SLTNode* tail = *plist;
SLTNode* prev = *plist;
if (pos == *plist)
{
SListPushFront(plist, x);
return;
}
else
{
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = newnode;
newnode->next = pos;
prev = prev->next;
}
}
// 单链表删除pos位置之后的值
void SListEraseAfter(SLTNode* pos)
{
assert(pos);
if (pos->next==NULL)
{
return;
}
else
{
SLTNode* next = pos->next;
pos->next = pos->next->next;
free(next);
}
}
//删除pos位置
void SListErase(SLTNode** plist,SLTNode* pos)
{
assert(pos);
if(*plist == pos)
{
SListPopFront(plist);
return;
}
else
{
SLTNode* prev = *plist;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
所有代码汇总如下
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDateType;
typedef struct SLTNode
{
SLTDateType data;
struct SLTNode* next;
}SLTNode;
// 动态申请一个节点
SLTNode* BuySLTNode(SLTDateType x);
// 单链表打印
void SLTPrint(SLTNode* phead);
//创建链表
SLTNode* CreateSLT(SLTNode* plist);
// 单链表的销毁
void SListDestroy(SLTNode* plist);
// 单链表尾插
void SLTPushBack(SLTNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SLTNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SLTNode** pplist);
// 单链表头删
void SListPopFront(SLTNode** pplist);
// 单链表查找
SLTNode* SListFind(SLTNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SLTNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SLTNode* pos);
//删除pos位置
void SListErase(SLTNode** plist, SLTNode* pos);
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"
SLTNode* BuySLTNode(SLTDateType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror(BuySLTNode);
exit(0);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("%d-> ", cur->data);
cur = cur->next;
}
printf("NULL");
}
SLTNode* CreateSLT(int n)
{
SLTNode* phead = NULL,*ptail = NULL;
for (int i = 0; i < n; i++)
{
SLTNode* newnode = BuySLTNode(i);
if (phead == NULL)
{
phead = ptail = newnode;
}
else
{
ptail->next = newnode;
ptail = newnode;
}
}
return phead;
}
void SListDestroy(SLTNode** plist)
{
SLTNode* cul = *plist;
while (cul)
{
SLTNode* next = cul->next;
free(cul);
cul = NULL;
}
*plist = NULL;
}
// 单链表尾插
void SLTPushBack(SLTNode** pplist, SLTDateType x)
{
SLTNode* newnode = BuySLTNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
SLTNode* tail = *pplist;
while (tail->next)
{
tail = tail->next;
}
tail->next = newnode;
}
}
// 单链表的头插
void SListPushFront(SLTNode** pplist, SLTDateType x)
{
SLTNode* newnode = BuySLTNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
// 单链表的尾删 int** p *p int*
void SListPopBack(SLTNode** pplist)
{
assert(*pplist);
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SLTNode* prev = NULL;
SLTNode* tail = *pplist;//这个错误是不能犯的 说明你对指针的理解不够
while (tail->next)
{
prev = tail;
tail = tail->next;
}
free(tail);
prev->next = NULL;
}
}
// 单链表头删
void SListPopFront(SLTNode** pplist)
{
assert(pplist);
SLTNode* next = (*pplist)->next;
free(*pplist);
*pplist = next;
}
// 单链表查找
SLTNode* SListFind(SLTNode* plist, SLTDateType x)
{
SLTNode* cul = plist;
while (cul!= NULL)
{
if (cul->data == x)
{
return cul;
}
cul = cul->next;
}
return NULL;
}
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SLTNode* pos, SLTDateType x)
{
assert(pos);
SLTNode* newnode = BuySLTNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
//在pos之前插入
void SListInsert(SLTNode** plist,SLTNode* pos, SLTDateType x)
{
assert(pos);
SLTNode* newnode = BuySLTNode(x);
SLTNode* tail = *plist;
SLTNode* prev = *plist;
if (pos == *plist)
{
SListPushFront(plist, x);
return;
}
else
{
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = newnode;
newnode->next = pos;
prev = prev->next;
}
}
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SLTNode* pos)
{
assert(pos);
if (pos->next==NULL)
{
return;
}
else
{
SLTNode* next = pos->next;
pos->next = pos->next->next;
free(next);
}
}
//删除pos位置
void SListErase(SLTNode** plist,SLTNode* pos)
{
assert(pos);
if(*plist == pos)
{
SListPopFront(plist);
return;
}
else
{
SLTNode* prev = *plist;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"
void test1()
{
SLTNode* plist = CreateSLT(3);
SLTPrint(plist);
}
void test2()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 100);
SLTPushBack(&plist, 200);
SLTPushBack(&plist, 300);
//free(plist);
SListPopFront(&plist);
SListPopFront(&plist);
SListPopFront(&plist);
SLTPrint(plist);
//SListPopBack(&plist);
//SLTPrint(plist);
}
test3()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 100);
SLTPushBack(&plist, 200);
SLTPushBack(&plist, 300);
//SListFind(plist, 200);
SListInsertAfter(plist,200, 5);
/*SListEraseAfter(plist, 5);*/
SListEraseAfter(plist,200);
//SListEraseAfter(plist, 200);
SLTPrint(plist);
}
test4()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPushBack(&plist, 5);
SLTNode* pos = SListFind(plist, 3);
if (pos)
{
SListInsertAfter(pos, 66);
SListEraseAfter(pos);
SListInsert(&plist, pos, 5);
//SListErase(&plist, pos);
SListEraseAfter(pos);
}
else
{
return;
}
SLTPrint(plist);
}
int main()
{
/*test4();*/
return 0;
}
今天的分享就到这里了,希望对宝子们有所帮助和提升。