目录
基本概念
什么是单链表(SLT)
图解
单链表的结构体类型的定义
代码实现
图解
输入新数据的单链表结点的生成
代码实现
图解
单链表的尾部插入
二级指针在单链表中基本操作的应用(以尾部插入为例)
图解
代码实现
图解
单链表的尾部删除
代码实现
图解
打印单链表内数据
代码实现
图解
用尾部插入和尾部删除对单链表进行修改
代码实现
输出结果
单链表的头部插入
代码实现
图解
编辑单链表的头部删除
代码实现
图解
用尾部插入和尾部删除对单链表进行修改
代码实现
输出结果
单链表的按值查找
代码实现
单链表的指定结点插入
代码实现
图解
单链表的指定结点删除
代码实现
图解
用指定结点插入和指定结点删除对单链表进行修改
代码实现
输出结果
单链表的销毁
代码实现
通俗点来说,单链表其实就是许多个结点通过指针连接起来,每个结点内包含一个数据以及指向下一个结点的指针,通过指针将每个结点连接起来,形成一条“链”。由于结点内只有指向下一个结点的指针,因此单链表只能从头往后访问结点。具有头指针指向头结点(第一个结点),尾结点(最后一个结点)指向空地址。
typedef int SLTdatatype; // 对数据的变量类型重新定义,方便后续只修改这一项
typedef struct SLTNode // 单链表结点的结构体类型的定义
{
SLTdatatype data; // 数据域
struct SLTNode* next; // 指针域
}SLTNode;
// 声明
SLTNode* BuySLTNode(SLTdatatype x);
// 定义
SLTNode* BuySLTNode(SLTdatatype x) // 创立输入新数据的结点
{
SLTNode* newnode = (SLTdatatype*)malloc(sizeof(SLTNode)); // 创建输入新的结点
if (newnode == NULL) // 判断新结点是否创建成功
{
perror("malloc fail");
return NULL;
}
newnode->data = x; // 给新结点输入新数据
newnode->next = NULL; // 给新结点的指针域输入空指针
return newnode; // 返回新结点的地址
}
// 调用
SLTNode* newnode = BuySLTNode(x); // 提供给单链表的其他操作使用
在单链表的尾结点后插入一个新结点。
当单链表为空时(即头指针为空),用尾部插入第一个结点时,若只是传入头指针进尾部插入的函数,让头指针指向新结点时,只是函数内头指针的“投影”头指针指向新结点,而头指针仍为空,而传入头指针的地址进尾部插入的函数,通过解引用即可访问头指针对它进行修改。
// 声明
void SLTPushBack(SLTNode** head, SLTdatatype x);
// 定义
void SLTPushBack(SLTNode** head, SLTdatatype x) // 尾部插入
{
assert(head); // 避免用户传错参数
SLTNode* newnode = BuySLTNode(x); // 创建输入新数据的结点
if (*head == NULL) // 单链表没有结点的情况
{
*head = newnode;
}
else // 单链表具有结点的情况
{
SLTNode* tail = *head; // 创建"尾"指针
while (tail->next != NULL)
{
tail = tail->next; // "尾"指针后移
}
tail->next = newnode; // 连接新结点
}
}
// 调用
SLTPushBack(&SList, x); // x是具体要输入的数据
删除单链表最后一个结点。
// 声明
void SLTPopBack(SLTNode** head);
// 定义
void SLTPopBack(SLTNode** head) // 尾部删除
{
assert(head); // 避免用户传错参数
assert(*head); // 单链表没有结点的情况下断言报错
if ((*head)->next == NULL) // 单链表只有一个结点的情况
{
free(*head); // 删除结点
*head = NULL;
}
else // 单链表有多个结点的情况
{
SLTNode* tail = *head; // 创建"尾"指针
while ( tail->next->next != NULL )
{
tail = tail->next; // "尾"指针后移
}
free(tail->next); // 删除尾部最后一个结点
tail->next = NULL;
}
}
// 调用
SLTPopBack(&SList);
// 声明
void SLTPrint(SLTNode* SList);
// 定义
void SLTPrint(SLTNode* SList) // 打印单链表内数据
{
SLTNode* cur = SList; // 创建了"遍历"指针
while (cur)
{
printf("%d->", cur->data); // 打印数据
cur = cur->next; // "遍历"指针后移
}
printf("NULL\n");
}
// 调用
SLTPrint(SList);
// 相关头文件
#include
#include
#include
// 需要的相关定义
// 单链表结点的结构体类型的定义
typedef int SLTdatatype; // 对数据的变量类型重新定义,方便后续只修改这一项
typedef struct SLTNode
{
SLTdatatype data; // 数据域
struct SLTNode* next; // 指针域
}SLTNode;
// 创立输入新数据的结点
SLTNode* BuySLTNode(SLTdatatype x)
{
SLTNode* newnode = (SLTdatatype*)malloc(sizeof(SLTNode)); // 创建输入新的结点
if (newnode == NULL) // 判断新结点是否创建成功
{
perror("malloc fail");
return NULL;
}
newnode->data = x; // 给新结点输入新数据
newnode->next = NULL; // 给新结点的指针域输入空指针
return newnode; // 返回新结点的地址
}
// 尾部插入
void SLTPushBack(SLTNode** head, SLTdatatype x)
{
assert(head); // 避免用户传错参数
SLTNode* newnode = BuySLTNode(x); // 创建输入新数据的结点
if (*head == NULL) // 单链表没有结点的情况
{
*head = newnode;
}
else // 单链表具有结点的情况
{
SLTNode* tail = *head; // 创建"尾"指针
while (tail->next != NULL)
{
tail = tail->next; // "尾"指针后移
}
tail->next = newnode; // 连接新结点
}
}
// 尾部删除
void SLTPopBack(SLTNode** head)
{
assert(head); // 避免用户传错参数
assert(*head); // 单链表没有结点的情况下断言报错
if ((*head)->next == NULL) // 单链表只有一个结点的情况
{
free(*head); // 删除结点
*head = NULL;
}
else // 单链表有多个结点的情况
{
SLTNode* tail = *head; // 创建"尾"指针
while (tail->next->next != NULL)
{
tail = tail->next; // "尾"指针后移
}
free(tail->next); // 删除尾部最后一个结点
tail->next = NULL;
}
}
// 打印单链表内数据
void SLTPrint(SLTNode* SList)
{
SLTNode* cur = SList; // 创建了"遍历"指针
while (cur)
{
printf("%d->", cur->data); // 打印数据
cur = cur->next; // "遍历"指针后移
}
printf("NULL\n");
}
// 主函数
SLTNode* SList = NULL; // 创建空的头指针
int main()
{
// 生成一个 1-5 的单链表
SLTPushBack(&SList, 1);
SLTPushBack(&SList, 2);
SLTPushBack(&SList, 3);
SLTPushBack(&SList, 4);
SLTPushBack(&SList, 5);
SLTPrint(SList);
// 删除最后一个结点
SLTPopBack(&SList);
SLTPrint(SList);
}
用尾部插入创建的单链表数据跟输入顺序一样,尾部删除就是从最后一个结点删除。
在单链表的头结点前插入一个新结点。
// 声明
void SLTPushFront(SLTNode** head, SLTdatatype x);
// 定义
void SLTPushFront(SLTNode** head, SLTdatatype x)// 头部插入
{
assert(head); // 避免用户传错参数
SLTNode* newnode = BuySLTNode(x); // 创建输入新数据的新结点
newnode->next = *head;
*head = newnode; // 单链表指针与新结点连接
}
// 调用
SLTPushFront(&SList, x); // x是具体要输入的数据
删除单链表的第一个结点。
// 声明
void SLTPopFront(SLTNode** head);
// 定义
void SLTPopFront(SLTNode** head)// 头部删除
{
assert(head); // 避免用户传错参数
assert(*head); // 单链表没有结点的情况下断言报错
SLTNode* first = *head; // 创建"头"指针
*head = first->next;
free(first); // 释放头结点
first = NULL;
}
// 调用
SLTPopFront(&SList);
// 相关头文件
#include
#include
#include
// 需要的相关定义
// 单链表结点的结构体类型的定义
typedef int SLTdatatype; // 对数据的变量类型重新定义,方便后续只修改这一项
typedef struct SLTNode
{
SLTdatatype data; // 数据域
struct SLTNode* next; // 指针域
}SLTNode;
// 创立输入新数据的结点
SLTNode* BuySLTNode(SLTdatatype x)
{
SLTNode* newnode = (SLTdatatype*)malloc(sizeof(SLTNode)); // 创建输入新的结点
if (newnode == NULL) // 判断新结点是否创建成功
{
perror("malloc fail");
return NULL;
}
newnode->data = x; // 给新结点输入新数据
newnode->next = NULL; // 给新结点的指针域输入空指针
return newnode; // 返回新结点的地址
}
// 头部插入
void SLTPushFront(SLTNode** head, SLTdatatype x)
{
assert(head); // 避免用户传错参数
SLTNode* newnode = BuySLTNode(x); // 创建输入新数据的新结点
newnode->next = *head;
*head = newnode; // 单链表指针与新结点连接
}
// 头部删除
void SLTPopFront(SLTNode** head)
{
assert(head); // 避免用户传错参数
assert(*head); // 单链表没有结点的情况下断言报错
SLTNode* first = *head; // 创建"头"指针
*head = first->next;
free(first); // 释放头结点
first = NULL;
}
// 打印单链表内数据
void SLTPrint(SLTNode* SList)
{
SLTNode* cur = SList; // 创建了"遍历"指针
while (cur)
{
printf("%d->", cur->data); // 打印数据
cur = cur->next; // "遍历"指针后移
}
printf("NULL\n");
}
// 主函数
SLTNode* SList = NULL; // 创建空的头指针
int main()
{
// 生成一个 5-1 的单链表
SLTPushFront(&SList, 1);
SLTPushFront(&SList, 2);
SLTPushFront(&SList, 3);
SLTPushFront(&SList, 4);
SLTPushFront(&SList, 5);
SLTPrint(SList);
// 删除第一个结点
SLTPopFront(&SList);
SLTPrint(SList);
}
用头部插入创建的单链表数据跟输入顺序相反,头部删除就是从第一个结点删除。
// 声明
SLTNode* SLTFind(SLTNode* head, SLTdatatype x);
// 定义
SLTNode* SLTFind(SLTNode* head, SLTdatatype x) // 按值查找
{
SLTNode* cur = head; // 创建"遍历"指针
while (cur)
{
if (cur->data == x) // 寻找单链表中与数据x匹配的结点
{
return cur; // 返回数据匹配结点的地址
}
cur = cur->next; // "遍历"指针后移
}
return NULL; // 单链表中找不到与数据x匹配的结点,返回空指针
}
// 调用
SLTNode* pos = SLTFind(SList,x); // x是想要在单链表内具体查找的数据
原理跟打印单链表内数据的代码类似,可参考其图解自己画图理解
通过按值查找,找到对应数据的结点位置进行插入(例如:找到数值为2的结点是2号结点,则在2号结点插入新数据,而原来的2号结点变为3号结点),若不理解可结合后续——用指定结点插入和指定结点删除的代码输出结果进行理解。
原理是先在指定结点后插入一个新结点,再将指定结点的数据与新结点的数据互换,做到在指定结点插入新结点的效果。
// 声明
void SLTInsert(SLTNode* pos, SLTdatatype x);
// 定义
void SLTInsert(SLTNode* pos, SLTdatatype x) // pos结点处插入
{
assert(pos);
SLTNode* newnode = BuySLTNode(x); // 创建新结点
newnode->next = pos->next; // 新结点与pos结点的下一个结点连接
pos->next = newnode; // pos结点与新结点连接
int temp = newnode->data; // 创建临时变量存储新结点内数据
newnode->data = pos->data; // 新结点内数据改为pos结点内数据
pos->data = temp; // pos结点内数据改为新结点内数据
}
// 调用
SLTInsert(pos,x); // x是具体要输入的数据
通过按值查找,找到对应数据的结点位置进行删除(例如:找到数值为2的结点是2号结点,则删除2号结点,而原来的3号结点变为新的2号结点),若不理解可结合后续——用指定结点插入和指定结点删除的代码输出结果进行理解。
原理是将指定结点的数据换成下一个结点的数据,删除下一个结点的,做到在指定结点删除的效果。
// 声明
void SLTErase(SLTNode* pos);
// 定义
void SLTErase(SLTNode* pos) // pos结点处删除
{
assert(pos);
assert(pos->next); // pos结点的下一个结点不能为空
SLTNode* temp = pos->next; // 创建临时指针指向pos指向结点的下一个结点
pos->data = temp->data; // pos结点内数据改为下一结点内数据
pos->next = temp->next; // pos结点与其下下个结点连接
free(temp); // 删除pos结点的下一个结点
temp = NULL;
}
// 调用
SLTErase(pos);
// 相关头文件
#include
#include
#include
// 需要的相关定义
// 单链表结点的结构体类型的定义
typedef int SLTdatatype; // 对数据的变量类型重新定义,方便后续只修改这一项
typedef struct SLTNode
{
SLTdatatype data; // 数据域
struct SLTNode* next; // 指针域
}SLTNode;
// 创立输入新数据的结点
SLTNode* BuySLTNode(SLTdatatype x)
{
SLTNode* newnode = (SLTdatatype*)malloc(sizeof(SLTNode)); // 创建输入新的结点
if (newnode == NULL) // 判断新结点是否创建成功
{
perror("malloc fail");
return NULL;
}
newnode->data = x; // 给新结点输入新数据
newnode->next = NULL; // 给新结点的指针域输入空指针
return newnode; // 返回新结点的地址
}
// 尾部插入
void SLTPushBack(SLTNode** head, SLTdatatype x)
{
assert(head); // 避免用户传错参数
SLTNode* newnode = BuySLTNode(x); // 创建输入新数据的结点
if (*head == NULL) // 单链表没有结点的情况
{
*head = newnode;
}
else // 单链表具有结点的情况
{
SLTNode* tail = *head; // 创建"尾"指针
while (tail->next != NULL)
{
tail = tail->next; // "尾"指针后移
}
tail->next = newnode; // 连接新结点
}
}
// 尾部删除
void SLTPopBack(SLTNode** head)
{
assert(head); // 避免用户传错参数
assert(*head); // 单链表没有结点的情况下断言报错
if ((*head)->next == NULL) // 单链表只有一个结点的情况
{
free(*head); // 删除结点
*head = NULL;
}
else // 单链表有多个结点的情况
{
SLTNode* tail = *head; // 创建"尾"指针
while (tail->next->next != NULL)
{
tail = tail->next; // "尾"指针后移
}
free(tail->next); // 删除尾部最后一个结点
tail->next = NULL;
}
}
// 打印单链表内数据
void SLTPrint(SLTNode* SList)
{
SLTNode* cur = SList; // 创建了"遍历"指针
while (cur)
{
printf("%d->", cur->data); // 打印数据
cur = cur->next; // "遍历"指针后移
}
printf("NULL\n");
}
// 按值查找
SLTNode* SLTFind(SLTNode* head, SLTdatatype x)
{
SLTNode* cur = head; // 创建"遍历"指针
while (cur)
{
if (cur->data == x) // 寻找单链表中与数据x匹配的结点
{
return cur; // 返回数据匹配结点的地址
}
cur = cur->next; // "遍历"指针后移
}
return NULL; // 单链表中找不到与数据x匹配的结点,返回空指针
}
// pos结点处插入
void SLTInsert(SLTNode* pos, SLTdatatype x)
{
assert(pos);
SLTNode* newnode = BuySLTNode(x); // 创建新结点
newnode->next = pos->next; // 新结点与pos结点的下一个结点连接
pos->next = newnode; // pos结点与新结点连接
int temp = newnode->data; // 创建临时变量存储新结点内数据
newnode->data = pos->data; // 新结点内数据改为pos结点内数据
pos->data = temp; // pos结点内数据改为新结点内数据
}
// pos结点处删除
void SLTErase(SLTNode* pos)
{
assert(pos);
assert(pos->next); // pos结点的下一个结点不能为空
SLTNode* temp = pos->next; // 创建临时指针指向pos指向结点的下一个结点
pos->data = temp->data; // pos结点内数据改为下一结点内数据
pos->next = temp->next; // pos结点与其下下个结点连接
free(temp); // 删除pos结点的下一个结点
temp = NULL;
}
// 主函数
SLTNode* SList = NULL; // 创建空的头指针
int main()
{
// 生成一个 1-5 的单链表
SLTPushBack(&SList, 1);
SLTPushBack(&SList, 2);
SLTPushBack(&SList, 3);
SLTPushBack(&SList, 4);
SLTPushBack(&SList, 5);
SLTPrint(SList);
SLTNode* pos1 = SLTFind(SList, 2); // 找到数值为2的结点
SLTInsert(pos1, 100); // 在数值为2的结点处插入数值为100的结点
SLTPrint(SList);
SLTNode* pos2 = SLTFind(SList, 100); // 找到数值为100的结点
SLTErase(pos2); // 删除数值为100的结点
SLTPrint(SList);
}
利用尾部删除,将单链表内的结点从后往前删除,直到最后删除所有结点,头指针置空。
// 声明
void SLTDestroy(SLTNode** SList);
// 定义
void SLTDestroy(SLTNode** SList) // 链表的销毁
{
assert(SList); // 避免用户传错
assert(*SList); // 判断链表是否为空
while (*SList) // 链表为空时停止循环
{
SLTPopBack(SList); // 利用尾部删除来删除结点
}
// 调用
SLTDestroy(&SList);