单链表的基本操作
·
1. 创建节点
每个节点包含两个部分:
- 数据域:存储节点的数据。
- 指针域:指向下一个节点的指针。
2. 头插法
- 操作:在链表的头部插入一个新节点。新节点的
next指针指向当前的头节点,然后将新节点设为头节点。 - 适用场景:快速在链表头部添加元素。
3. 尾插法
- 操作:在链表的尾部插入一个新节点。需要先找到链表的最后一个节点,然后让最后一个节点的
next指针指向新节点。 - 适用场景:需要保持链表顺序或添加元素到链表末尾时。
4. 头删法
- 操作:删除链表的第一个节点。头节点的
next指针指向新的头节点,并释放原头节点。 - 适用场景:快速删除链表的第一个元素。
5. 尾删法
- 操作:删除链表的最后一个节点。需要找到倒数第二个节点,将其
next指针设为NULL,然后释放最后一个节点。 - 适用场景:需要删除链表末尾的元素。
6. 查找节点
- 操作:从链表的头部开始遍历,找到第一个数据域匹配目标值的节点,并返回该节点的指针。如果找不到,返回
NULL。 - 适用场景:在链表中查找特定值的节点。
7. 插入节点
- 在指定位置之前插入:在链表中找到指定位置的节点,将新节点的
next指针指向该节点,并将前一个节点的next指针指向新节点。 - 在指定位置之后插入:直接将新节点插入到指定节点的后面,修改相应指针。
- 适用场景:在链表的特定位置插入元素。
8. 删除节点
- 删除指定节点:找到要删除的节点及其前一个节点,将前一个节点的
next指针指向要删除节点的下一个节点,然后释放要删除的节点。 - 删除指定节点之后的节点:直接修改指定节点的
next指针,跳过下一个节点,然后释放被跳过的节点。 - 适用场景:从链表中删除特定位置的元素。
9. 遍历链表
- 操作:从链表的头节点开始,依次访问每个节点,直到访问到链表末尾 (
next为NULL)。 - 适用场景:需要对链表中的每个元素进行操作时。
10. 销毁链表
- 操作:依次释放链表中每个节点的内存,直到整个链表被清空。
- 适用场景:链表不再使用,需要释放内存时。
下面我将完整的操作部分分为3个代码区部分;
1结点和链表的定义 具体操作函数的申明 (.h头文件)
- SLTDataType:定义链表节点中存储的数据类型,这里设定为
int。 - SLTNode:链表节点的结构体,包含数据和指向下一个节点的指针,用于构建链表。
- 函数声明:
- SLTPrint:打印链表内容的函数。
- SLTPushBack:在链表的尾部插入新节点。
- SLTPushFront:在链表的头部插入新节点。
- SLTPopBack:删除链表最后一个节点。
- SLTPopFront:删除链表第一个节点。
- SLTFind:查找链表中第一个与给定值相等的节点,并返回该节点的指针。
- SLTInsert:在给定节点之前插入新节点。
- SLTInsertAfter:在指定节点后插入新节点。
- SLTErase:删除指定节点。
- SLTEraseAfter:删除指定节点之后的节点。
- SListDesTroy:销毁整个链表并释放内存。
- 代码1
#pragma once // 确保头文件只被包含一次 #include<stdio.h> #include<stdlib.h> #include<assert.h> // 定义数据类型为 int typedef int SLTDataType; // 定义链表节点结构体 typedef struct SListNode { SLTDataType data; // 节点的数据部分 struct SListNode* next; // 指向下一个节点的指针 } SLTNode; // 打印链表函数声明 void SLTPrint(SLTNode* phead); // 尾插法:在链表的尾部插入一个新节点 void SLTPushBack(SLTNode** pphead, SLTDataType x); // 头插法:在链表的头部插入一个新节点 void SLTPushFront(SLTNode** pphead, SLTDataType x); // 尾删:删除链表的最后一个节点 void SLTPopBack(SLTNode** pphead); // 头删:删除链表的第一个节点 void SLTPopFront(SLTNode** pphead); // 查找:在链表中查找值为 x 的节点,返回该节点的指针 SLTNode* SLTFind(SLTNode* phead, SLTDataType x); // 在指定位置 pos 之前插入数据 x void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x); // 在指定位置 pos 之后插入数据 x void SLTInsertAfter(SLTNode* pos, SLTDataType x); // 删除指定节点 pos void SLTErase(SLTNode** pphead, SLTNode* pos); // 删除 pos 之后的节点(即 pos 的下一个节点) void SLTEraseAfter(SLTNode* pos); // 销毁链表:释放链表的所有节点内存 void SListDesTroy(SLTNode** pphead);2是上面操作函数的具体实现过程 实现了一个单链表的基本操作,包括节点的创建、插入、删除、查找、遍历和销毁等(.c源文件)。
-
代码2
#define _CRT_SECURE_NO_WARNINGS 1 #include"SeqList.h" // 打印链表 void SLTPrint(SLTNode* phead) { SLTNode* pcur = phead; while (pcur) // 当 pcur 不为 NULL 时循环 { printf("%d->", pcur->data); // 打印当前节点的数据 pcur = pcur->next; // 移动到下一个节点 } printf("NULL\n"); // 打印链表结束标志 } // 创建新节点 SLTNode* SLTBuyNode(SLTDataType x) { SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode)); // 为新节点分配内存 if (newnode == NULL) { perror("开辟空间失败"); // 如果分配失败,输出错误信息 exit(1); // 退出程序 } newnode->data = x; // 设置新节点的数据 newnode->next = NULL; // 设置新节点的 next 指针为 NULL return newnode; // 返回新节点 } // 尾插法:在链表尾部插入一个新节点 void SLTPushBack(SLTNode** pphead, SLTDataType x) { assert(pphead); // 断言 pphead 不为 NULL SLTNode* newnode = SLTBuyNode(x); // 创建新节点 if (*pphead == NULL) { // 如果链表为空 *pphead = newnode; // 新节点成为链表的头节点 } else { // 找到链表的尾节点 SLTNode* pback = *pphead; while (pback->next) // 遍历链表直到尾节点 { pback = pback->next; // 移动到下一个节点 } pback->next = newnode; // 将新节点插入到尾节点之后 } } // 头插法:在链表头部插入一个新节点 void SLTPushFront(SLTNode** pphead, SLTDataType x) { assert(pphead); // 断言 pphead 不为 NULL SLTNode* newnode = SLTBuyNode(x); // 创建新节点 newnode->next = *pphead; // 新节点的 next 指针指向当前头节点 *pphead = newnode; // 更新头节点为新节点 } // 尾删法:删除链表的最后一个节点 void SLTPopBack(SLTNode** pphead) { // 链表不能为空 assert(pphead && *pphead); // 链表只有一个节点 if ((*pphead)->next == NULL) // -> 优先级高于* { free(*pphead); // 释放头节点 *pphead = NULL; // 将头节点设为 NULL } else { // 链表有多个节点 SLTNode* prev = *pphead; SLTNode* ptail = *pphead; while (ptail->next) // 遍历链表直到尾节点 { prev = ptail; // 记录当前节点为前一个节点 ptail = ptail->next; // 移动到下一个节点 } // prev ptail free(ptail); // 释放尾节点 ptail = NULL; // 将尾节点设为 NULL prev->next = NULL; // 更新前一个节点的 next 指针为 NULL } } // 头删法:删除链表的第一个节点 void SLTPopFront(SLTNode** pphead) { // 链表不可以为空 assert(pphead && *pphead); // 找出要删除的头结点的下一个结点 SLTNode* next = (*pphead)->next; free(*pphead); // 释放头节点 *pphead = next; // 更新头节点为下一个节点 } // 查找:在链表中查找第一个匹配指定数据的节点 SLTNode* SLTFind(SLTNode* phead, SLTDataType x) { SLTNode* pcur = phead; while (pcur) // 遍历链表 { if (pcur->data == x) { // 如果找到匹配的节点 return pcur; // 返回该节点 } pcur = pcur->next; // 移动到下一个节点 } return NULL; // 如果未找到,返回 NULL } // 在指定位置之前插入数据 // 在头结点之前插入,这样头结点就会改变 // 我们需要找到指定位置的前一个结点,但是 pos 只能找到自己的下一个结点 // 所有需要头结点找到 pos 指定位置的前一个结点 void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) { assert(pphead && *pphead); // 断言 pphead 和 *pphead 不为 NULL assert(pos); // 断言 pos 不为 NULL SLTNode* newnode = SLTBuyNode(x); // 创建新节点 // 若 pos == *pphead;说明是头插 if (pos == *pphead) { SLTPushFront(pphead, x); // 调用头插函数 } else { SLTNode* prev = *pphead; // prev 是 pos 的前一个结点 while (prev->next != pos) // 遍历链表直到找到 pos 的前一个节点 { prev = prev->next; // 移动到下一个节点 } // prev -> newnode -> pos 插入 newnode newnode->next = pos; // 新节点的 next 指针指向 pos prev->next = newnode; // 前一个节点的 next 指针指向新节点 } } // 在指定位置之后插入数据 void SLTInsertAfter(SLTNode* pos, SLTDataType x) { assert(pos); // 断言 pos 不为 NULL SLTNode* newnode = SLTBuyNode(x); // 创建新节点 // pos -> newnode -> pos->next newnode->next = pos->next; // 新节点的 next 指针指向 pos 的下一个节点 pos->next = newnode; // pos 的 next 指针指向新节点 } // 删除 pos 节点 void SLTErase(SLTNode** pphead, SLTNode* pos) // 用不到头结点 { assert(pphead && *pphead); // 断言 pphead 和 *pphead 不为 NULL assert(pos); // 断言 pos 不为 NULL // pos 是头结点/pos 不是头结点 if (pos == *pphead) { // 头删 SLTPopFront(pphead); // 调用头删函数 } else { SLTNode* prev = *pphead; while (prev->next != pos) // 遍历链表直到找到 pos 的前一个节点 { prev = prev->next; // 移动到下一个节点 } // prev pos pos->next prev->next = pos->next; // 前一个节点的 next 指针指向 pos 的下一个节点 free(pos); // 释放 pos 节点 pos = NULL; // 将 pos 设为 NULL } } // 删除 pos 之后的节点 void SLTEraseAfter(SLTNode* pos) { assert(pos && pos->next); // 断言 pos 和 pos->next 不为 NULL SLTNode* del = pos->next; // pos del del->next pos->next = del->next; // pos 的 next 指针指向 del 的下一个节点 free(del); // 释放 del 节点 del = NULL; // 将 del 设为 NULL } // 销毁链表 void SListDesTroy(SLTNode** pphead) { assert(pphead && *pphead); // 断言 pphead 和 *pphead 不为 NULL SLTNode* pcur = *pphead; while (pcur) // 遍历链表 { SLTNode* next = pcur->next; // 保存下一个节点 free(pcur); // 释放当前节点 pcur = next; // 移动到下一个节点 } // pcur *pphead = NULL; // 将头节点设为 NULL }3打印链表的操作(.c源文件)
代码3
#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h" // 包含头文件,定义了链表相关的结构和函数
// 测试1:手动创建节点并连接它们
void SListTest01()
{
// 链表是由一个一个的节点组成
// 创建几个节点
SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode)); // 创建第一个节点
node1->data = 1; // 设置节点数据
SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode)); // 创建第二个节点
node2->data = 2; // 设置节点数据
SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode)); // 创建第三个节点
node3->data = 3; // 设置节点数据
SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode)); // 创建第四个节点
node4->data = 4; // 设置节点数据
// 将四个节点连接起来
node1->next = node2; // node1的下一个节点是node2
node2->next = node3; // node2的下一个节点是node3
node3->next = node4; // node3的下一个节点是node4
node4->next = NULL; // node4的下一个节点为空,表示链表结束
// 调用链表的打印函数
SLTNode* plist = node1; // 链表的起始节点
SLTPrint(plist); // 打印链表内容
}
// 测试2:使用链表操作函数进行增删查改
void SListTest02() {
// 尾插法:从链表尾部插入数据
SLTNode* plist = NULL; // 初始化链表为空
SLTPushBack(&plist, 1); // 尾插1
SLTPushBack(&plist, 2); // 尾插2
SLTPushBack(&plist, 3); // 尾插3
SLTPushBack(&plist, 4); // 尾插4
SLTPrint(plist); // 打印链表内容
// 头插法:从链表头部插入数据
SLTPushFront(&plist, 6); // 头插6
SLTPushFront(&plist, 7); // 头插7
SLTPrint(plist); // 打印链表内容
// 尾部删除:删除链表最后一个节点
SLTPopBack(&plist);
SLTPrint(plist); // 打印链表内容
// 头部删除:删除链表第一个节点
SLTPopFront(&plist);
SLTPrint(plist); // 打印链表内容
// 查找指定位置元素
SLTNode* found = SLTFind(plist, 2); // 查找元素2的节点
if (found) {
printf("元素下标是: %d\n", found->data); // 打印找到的元素
}
else {
printf("没有找到\n");
}
// 在指定位置之前插入数据
SLTInsert(&plist, plist->next, 5); // 在第二个节点之前插入5
SLTPrint(plist); // 打印链表内容
// 在指定位置之后插入数据
SLTInsertAfter(plist->next->next, 7); // 在第三个节点之后插入7
SLTPrint(plist); // 打印链表内容
// 删除指定位置的节点
SLTErase(&plist, plist->next); // 删除第二个节点
SLTPrint(plist); // 打印链表内容
// 删除指定位置之后的节点
SLTEraseAfter(plist->next); // 删除第三个节点之后的节点
SLTPrint(plist); // 打印链表内容
}
int main() {
//SListTest01(); // 调用测试1函数
SListTest02(); // 调用测试2函数
return 0;
}
更多推荐



所有评论(0)