单链表的定义与基本操作
单链表操作实现1.什么是单链表?
单链表是一种常见的线性数据结构,由一系列节点组成,每个节点包含两个部分:数据域 和 指针域。数据域存储实际数据,指针域指向下一个节点。在单链表中,数据元素可以非连续地存储在内存中,而节点之间通过指针相互连接。
2.代码实现
链表的创建、插入、删除、查找等常用操作。
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef int DataType_t;
//构造链表的节点,列表中所有的数据类型应该是相同的
typedef struct LinkedList
{
DataType_t data;//节点的数据域
struct LinkedList*next;//结点的指针域
}LList_t;
//创建一个空链表,空链表应该有一个头节点,对链表进行初始化
LList_t* LList_Create(void)
{
//1.给头节点申请空间
LList_t *Head = (LList_t *)calloc(1,sizeof(LList_t));
if(NULL == Head)
{
perror("Calloc memory for the Head is failed!\n");
exit(-1);
}
//头节点初始化,头节点是不存储有效内容
Head->next = NULL;
return Head;
}
//生成新的有效结点
LList_t* LList_NewNode(DataType_t data)
{
LList_t *New = (LList_t *)calloc(1,sizeof(LList_t));
if(NULL == New)
{
perror("Calloc memory for the New is failed!\n");
exit(-1);
}
//新节点初始化
New->next = NULL;
New->data = data;
return New;
}
//在链表头部插入结点
bool LList_HeadInsert(LList_t *Head,DataType_t data)
{
LList_t *New = LList_NewNode(data);
if(NULL == New)
{
perror("Create NewNode is Failed !\n");
return false;
}
//若链表无有效结点
if(NULL == Head->next)
{
Head->next = New;
return true;
}
//在头结点后插入结点
New->next= Head->next;
Head->next = New;
return true;
}
//遍历链表每个结点
void LList_Print(LList_t *Head)
{
if (Head == NULL || Head->next == NULL) {
printf("链表为空!\n");
return;
}
LList_t *current = Head->next;
printf("链表内容: ");
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
//在链表尾部插入结点
bool LList_TailInsert(LList_t *Head,DataType_t data)
{
LList_t *New = LList_NewNode(data);
if(NULL == New)
{
perror("Create NewNode is Failed !\n");
return false;
}
//找到最后一个结点位置
while(Head->next != NULL)
{
Head = Head->next;
}
Head->next = New;
return true;
}
//根据结点数据域的值来指定在目标结点后插入新结点
bool LList_DestInsert(LList_t *Head,DataType_t dest,DataType_t data)
{
LList_t *New = LList_NewNode(data);
if(NULL == New)
{
perror("Create NewNode is Failed !\n");
return false;
}
//链表只有头结点,新结点直接放在头结点之后
if(NULL == Head->next)
{
Head->next = New;
return true;
}
LList_t *current = Head->next;
while((current != NULL) && (current->data != dest))
{
current = current->next;
}
//current指到了最后一个结点的next
if (current == NULL)
{
printf("Destination node not found!\n");
free(New); // 插入失败,释放已申请的内存
return false;
}
//找到了数据域data值等于dest的结点
New ->next = current->next;
current->next = New;
return true;
}
//删除链表尾部结点
bool LList_TailDel(LList_t *Head)
{
if(NULL == Head->next)
{
printf("链表空!\n");
return false;
}
LList_t *current = Head->next;
LList_t *ex_current = Head; // 初始化为Head,用于删除操作
// 遍历链表,找到尾节点和它的前一个节点
while(current->next != NULL)
{
ex_current = current;
current = current->next;
}
// 删除尾节点
ex_current->next = NULL; // 断开与尾节点的连接
free(current); // 释放尾节点的内存
return true;
}
//删除链表头(头结点之后)的结点
bool LList_HeadDel(LList_t *Head)
{
if(NULL == Head->next)
{
printf("链表空!\n");
return false;
}
LList_t *current = Head->next;
Head->next = Head->next->next;
free(current);
current = NULL;
return true;
}
//根据结点数据域的值来删除指定目标结点
bool LList_DestDel(LList_t *Head, DataType_t dest)
{
if (NULL == Head->next)
{
printf("链表空!\n");
return false;
}
LList_t *current = Head->next;
LList_t *ex_current = Head;
// 遍历链表,直到找到目标节点或链表结束
while (current != NULL && current->data != dest)
{
ex_current = current;
current = current->next;
}
// 检查是否找到目标节点
if (current == NULL)
{
printf("没有在链表找到要删除的指定结点!\n");
return false;
}
// 删除目标节点
ex_current->next = current->next;
free(current);
current = NULL;
return true;
}其他辅助函数
1.删除最小值结点
bool LList_MinDel(LList_t *Head)
{
// 判断链表是否为空:如果头节点的 next 为 NULL,说明链表没有有效节点
if (NULL == Head->next)
{
printf("链表空!\n");
return false;
}
// 定义当前遍历节点 current,从第一个有效节点开始
LList_t *current = Head->next;
LList_t *pre_current = Head;
// 定义最小值节点 MinNode,初始为第一个有效节点
LList_t *MinNode = current;
LList_t *pre_MinNode = Head;
// 遍历链表,寻找最小值节点
while (current != NULL)
{
// 如果当前节点的 data 小于最小值节点的 data
if(current->data < MinNode->data)
{
// 更新最小值节点及其前驱节点
MinNode = current;
pre_MinNode = pre_current;
}
// 继续向后遍历
pre_current = current;
current = current->next;
}
// 找到最小值节点后,将其从链表中删除
pre_MinNode->next = MinNode->next;
free(MinNode);
return true;
}2.查找倒数第 k 个节点(快慢指针法)
int findKthFromEnd(LList_t *Head, int k)
{
if (Head == NULL || k <= 0) {
printf("无效输入:k必须为正整数且链表不能为空\n");
return 0;
}
LList_t *fast = Head;
LList_t *slow = Head;
int steps = 0;
// fast 先走 k 步
while (steps < k) {
if (fast == NULL) {
printf("链表长度不足 %d 个节点,无法查找倒数第 %d 个节点\n", k, k);
return 0;
}
fast = fast->next;
steps++;
}
// 如果 fast 已经为 NULL,说明链表正好有 k 个节点
if (fast == NULL) {
printf("倒数第 %d 个结点的 data 值为: %d\n", k, slow->data);
return 1;
}
// 同时移动 fast 和 slow
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
// 此时 slow 指向倒数第 k 个节点
printf("倒数第 %d 个结点的 data 值为: %d\n", k, slow->data);
return 1;
}快慢指针经典算法。fast 先走 k 步,然后 slow 和 fast 一起走,当 fast 到达末尾时,slow 正好在倒数第 k 个位置。
3.主函数测试
int main(int argc, char const *argv[])
{
// 创建链表
printf("=== 创建空链表 ===\n");
LList_t *head = LList_Create();
if (head != NULL) {
printf("链表创建成功!\n");
}
LList_Print(head);
// 测试头插法
printf("\n=== 测试头插法 ===\n");
LList_HeadInsert(head, 10);
LList_HeadInsert(head, 20);
LList_HeadInsert(head, 30);
LList_Print(head);// 预期结果: 30 20 10
// 测试尾插法
printf("\n=== 测试尾插法 ===\n");
LList_TailInsert(head, 40);
LList_TailInsert(head, 50);
LList_Print(head);// 预期结果: 30 20 10 40 50
// 测试指定位置插入
printf("\n=== 测试指定位置插入 ===\n");
LList_DestInsert(head, 20, 25);// 在20后插入25
LList_Print(head);// 预期结果: 30 20 25 10 40 50
LList_DestInsert(head, 100, 99); // 尝试插入到不存在的节点后
// 测试查找倒数第k个节点
printf("\n=== 测试查找倒数第k个节点 ===\n");
findKthFromEnd(head, 1);// 预期结果: 50
findKthFromEnd(head, 3);// 预期结果: 10
findKthFromEnd(head, 10); // 预期结果: 链表长度不足
// 测试删除头节点
printf("\n=== 测试删除头节点 ===\n");
LList_HeadDel(head);
LList_Print(head);// 预期结果: 20 25 10 40 50
// 测试删除尾节点
printf("\n=== 测试删除尾节点 ===\n");
LList_TailDel(head);
LList_Print(head);// 预期结果: 20 25 10 40
// 测试删除指定节点
printf("\n=== 测试删除指定节点 ===\n");
LList_DestDel(head, 25);
LList_Print(head);// 预期结果: 20 10 40
LList_DestDel(head, 100); // 尝试删除不存在的节点
// 添加几个节点用于测试删除最小值
printf("\n=== 测试删除最小值节点 ===\n");
LList_TailInsert(head, 5);
LList_HeadInsert(head, 35);
LList_TailInsert(head, 15);
LList_Print(head);// 预期结果: 35 20 10 40 5 15
LList_MinDel(head); // 删除最小值5
LList_Print(head);// 预期结果: 35 20 10 40 15
LList_MinDel(head); // 删除最小值10
LList_Print(head);// 预期结果: 35 20 40 15
// 释放链表内存
printf("\n=== 释放链表内存 ===\n");
while (head->next != NULL) {
LList_HeadDel(head);
}
free(head);
head = NULL;
printf("链表已释放\n");
return 0;
}测试结果:
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]