链表:按需申请释放
注意:链式结构在逻辑上是连续的,但是在物理上不一定连续。
//signal list table 单链表
//signal link table 单链表
typedef int SLTataType;
typedef struct SListNode //signal 单的
{
SLTataType data; //数据域
struct SListNode* next; //指针域
}SLTNode;
先来浅浅写一个打印函数
void PrintSLT(SLTNode * phead)
{
SLTNode * cur = phead;
while (cur == NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
初始化链表
int n;
printf("请输入链表的长度:");
scanf("%d", &n);
printf("\n请依次输入每个结点的值:");
由于需要定义一个结构体指针来存放链表中第一个数据的地址,因此
SLTNode* plist = NULL;
使用for循环来完成链表数据的输入,但是由于后面的诸多函数(例如增删查改)均需要节点函数,因此我们先来写一个节点函数
SLTNode* BuySListNode(SLTDataType x)
{
SLTNode* newnode =(SLTNode *) malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
完成节点函数后,利用for循环实现链表的连接:
for (size_t i = 0; i < n; i++)
{
int val;
scanf("%d", &val);
SLTNode* newnode = BuySListNode(val);
}
连接完成以后,我们考虑到,假如 plist==NULL;
if (plist == NULL)
{
plist = newnode;
}
但是假如plist != NULL,则有头插和尾插两种情况,但由于尾插情况较为复杂,而且我们现在是在初始化链表,因此我们先讨论头插情况。
物理图如下:
代码实现如下:
newnode->next = plist;
plist = newnode;
即代码汇总为
for (size_t i = 0; i < n; i++)
{
int val;
scanf("%d", &val);
SLTNode* newnode = BuySListNode(val);
// 头插
newnode->next = plist;
plist = newnode;
}
那么问题来了,假如链表为空,需要对plist == NULL,进行分情况讨论吗,答案是不需要的,咱们看图说话。
整体代码如下:
void PrintSLT(SLTNode* phead)
{
SLTNode * cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
SLTNode* BuySListNode(SLTDataType x)
{
SLTNode* newnode =(SLTNode *) malloc(sizeof(SLTDataType));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void Test1()
{
int n;
printf("请输入链表的长度:");
scanf("%d", &n);
printf("\n请依次输入每个结点的值:");
SLTNode* plist = NULL;
for (size_t i = 0; i < n; i++)
{
int val;
scanf("%d",& val);
SLTNode* newnode = BuySListNode(val);
//头插
newnode->next = plist;
plist = newnode;
}
PrintSLT(plist);
}
int main()
{
Test1();
return 0;
}
运行如下:
现在来实行尾插 :
代码实现如下:
void SLTPushBack(SLTNode* phead, SLTDataType x)
{
//phead是形参 plist是实参
SLTNode* newnode = BuySListNode(x);
while (phead->next)
{
phead = phead->next;
}
phead->next = newnode;
}
运行如下:
现在又有一个新的问题来了,假如链表为空,那么要怎么插入呢?
此时就需要改变结构体指针,具体参考如下
void SLTPushBack(SLTNode** phead, SLTDataType x)
{
//phead是形参 plist是实参
SLTNode* newnode = BuySListNode(x);
if (*phead == NULL)
{
*phead = newnode;
}
else
{
SLTNode* tail = *phead;
while (tail->next)
{
tail = tail->next;
}
tail->next = newnode;
}
}
小tips:
1.改变结构体,需要用结构体指针
2.改变结构体指针, 需要用结构体指针的指针
头插尾插都写好了,我们再来写尾删头删
先来看结构图:
代码实现如下:
void SLTPopBack(SLTNode** phead, SLTDataType x)
{
//1.空
assert(*phead);//暴力检查
//2.一个节点
//3.一个以上节点
if ((*phead)->next == NULL)
{
free(*phead);
*phead = NULL;
}
else
{
//法一:
SLTNode* tailPrev = NULL;//表示tail前的那个结构体
SLTNode* tail = *phead;
while (tail->next)
{
tailPrev = tail;
tail = tail->next;
}
free(tail);
tailPrev->next = NULL;
//法二:
SLTNode* tail = *phead;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
运行如下:
头删
代码实现如下:
void SLTPopFront(SLTNode** phead)
{
//空
assert(*phead);
//非空
SLTNode* newhead = (*phead)->next;
free(*phead);
*phead = newhead;
}
运行如下:
接下来我们要进行:
//在pos位置插入x
void SLTInsert(SLTNode** phead, SLTNode* pos, SLTDataType x);
//在pos以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos位置
void SLTErase(SLTNode** phead, SLTNode* pos, SLTDataType x);
//删除pos的后一个位置
void SLTEraseAfter(SLTNode* pos, SLTDataType x);
欲知后事如何,请看下回分解,今天就先到这里了。