1.尾插函数
非空链表的代码示例
在SList.h写SLTPushBack函数的声明
下面的代码写入SList.c中
void SLTPushBack(SLTNode* phead, SLTDataType x)
{
SLTNode*newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc");
return;//错误返回
}
//为结点初始化
newnode->data = x;
newnode->next = NULL;
//找尾指针
SLTNode* tail = phead;//定义尾指针初始化为头指针的值
while (tail != NULL)
{
tail = tail->next;
}
tail->next = newnode;//改写原来的尾结点存储的指针为新尾节点的指针
}
注意
1.函数的一开始不需要断言,空链表也能尾插
2.尾插要提供新的结点(newnode)(内含数据和指针),需要开辟内存空间,之后初始化结点,并和前一个结点联系上(称为结点的链接)
3.一个典型的找尾指针的错误代码
SLTNode* tail = phead;//定义尾指针初始化为头指针的值
while (tail != NULL)
{
tail = tail->next;
}
tail = newnode;
只有当tail为NULL时才能退出循环,貌似找到了尾指针,出了函数局部变量会销毁,其实新结点是链接不上的!
将tail = newcode;改为tail->next = newcode;才可以!
非空链表的尾插核心:改写原来的尾结点存储的指针为新尾节点的指针
附:物理结构图
像下面这样,新结点是链接不上的
空链表的尾插
直接让phead指向新结点
void SLTPushBack(SLTNode* phead, SLTDataType x)
{
SLTNode*newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc");
return;//错误返回
}
//为结点初始化
newnode->data = x;
newnode->next = NULL;
if (phead == NULL)
{
phead = newnode;
}
else
{
//找尾指针
SLTNode* tail = phead;//定义尾指针初始化为头指针的值
while (tail != NULL)
{
tail = tail->next;
}
tail->next = newnode;//改写原来的尾结点存储的指针
}
}
设计main.c
#include "SList.h"
void TestSList1()
{
SLTNode* plist = NULL;
SLTPushBack(plist, 1);
SLTPushBack(plist, 2);
SLTPushBack(plist, 3);
SLTPushBack(plist, 4);
SLTPushBack(plist, 5);
SLTPushBack(plist, 6);
SLTPushBack(plist, 7);
SLTPushBack(plist, 8);
SLTPrint(plist);
}
int main()
{
TestSList1();
return 0;
}
运行后发现什么输出都没有
问题发现
VS2022进入调试模式,打开监视窗口
在第37行卡住了,F11无法继续调试下去,没有反应
问题分析
进入main函数后,调用了TestSList1函数,设plist为NULL,之后调用SLTPushBack函数,malloc开辟空间后,为newnode赋了一个地址,进入if语句,newnode不为NULL,则为newnode结构体中的data和next初始化,进入if语句,phead为NULL,则将newnode赋值给phead,SLTPushBack函数返回后,phead仍然为空指针!也就是说,SLTPushBack函数并没有改变phead的值!
可以想到,这是函数的传值调用
解决方法
将传值调用改为传址调用即可(有关两种调用的概念参见29.【C语言】函数系列中 自定义函数)
修改后的完整的代码
SList.c
#include "SList.h"
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL");//读到链表的结尾存储的为空指针
}
//pphead为指向phead的指针(pphead为二级指针)
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc");
return;//错误返回
}
//为结点初始化
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;//改写原来的尾结点存储的指针
}
}
注:想修改plist,必须用plist的地址,即pphead存储的值
main.c
#include "SList.h"
void TestSList1()
{
SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTPushBack(&plist, 5);
SLTPushBack(&plist, 6);
SLTPushBack(&plist, 7);
SLTPushBack(&plist, 8);
SLTPrint(plist);
}
int main()
{
TestSList1();
return 0;
}
SList.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
void SLTPrint(SLTNode* phead);
void SLTPushBack(SLTNode** pphead, SLTDataType x);
注意头文件的函数声明也要改过来
2.头插函数
非空链表的代码示例
在SList.h写SLTPushFront函数的声明
SList.c写入
头插函数也需要开辟空间并初始化结点,因此可以单独将此过程封装为一个函数BuySLTNode
SLTNode* BuySLTNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc");
return NULL;//错误返回
}
//为结点初始化
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySLTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
3.用VS的监视窗口来查看链表
输入plist