当前位置: 首页 > news >正文

数据结构与算法-单链表专题

一. 链表的概念和结构

概念:链表是物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表也是线性表中的一种

结构:链表是由一个一个的节点组成

节点有时由数据指向下一个节点的指针组成

二. 单链表的实现

2.1 定义链表的节点的结构

SList.h:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLDataType;//链表节点的创建
typedef struct SListNode
{SLDataType data;struct SListNode* next;
}SLTNode;

2.2 链表的初始化

add.c:

//初始化
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;
node2->next = node3;
node3->next = node4;
node4->next = NULL;

首先要先开辟空间,由于单链表中节点的空间无需再减少或者扩容,所以不需要使用realloc,用malloc进行创建就好

这里的节点都需要单独创建

然后节点结构体空间创建好后,放入初始化的数据(1,2,3,4)

之后就需要将各自创建的节点通过指针来进行链接,达到逻辑结构上的顺序结构,这样就形成了单链表

别忘了最后的节点指向下一个节点的指针需要变成空指针

2.3 单链表使用需要的函数

2.3.1 打印

//打印
void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}

通过next指针进行循环判断条件

2.3.2 创建新的节点

SLTNode* SLTBuyNode(SLDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail");exit(1);}newnode->next = NULL;newnode->data = x;return newnode;
}

这是为了插入新元素而开辟空间形成新节点的函数

2.3.3 尾插

//尾插
void SLTPushBack(SLTNode** pphead, SLDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);if (*pphead == NULL){*pphead = newnode;}else{SLTNode* ptail = *pphead;while (ptail->next == NULL){ptail = ptail->next;}ptail->next = newnode;}
}

首先注意此处形参和实参都是二级指针。

因为尾插操作其中需要改变一级指针,这样的话传一级指针就是传值调用,所以指针就不会改变,于是需要传地址调用,这样才能对原来的指针进行更改。

assert是为了防止传过来的是个空指针,这样ptail就无法使用了,因为空指针无法进行解引用

然后对单链表是否为空进行验证,为空就直接将新创建的节点为单链表的首节点继续使用

后面就是对尾节点的寻找以及将新创建的节点接连到后面

2.3.4 头插

//头插
void SLTPushFront(SLTNode** pphead, SLDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;*pphead = newnode;
}

跟尾插差不多,只不过没必要寻找尾节点,直接将传过来的首节点前进行插入新节点就可以

2.3.5 尾删

//尾删
void SLTPopBack(SLTNode** pphead)
{//链表不能为空assert(pphead && *pphead);//链表只有一个节点if ((*pphead)->next == NULL) //-> 优先级高于*{free(*pphead);*pphead = NULL;}else {//链表有多个节点SLTNode* prev = *pphead;SLTNode* ptail = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}//prev ptailfree(ptail);ptail = NULL;prev->next = NULL;}}

分为三种情况

第一种:链表为空。无需进行尾删,直接退出

第二种:链表只有一个节点。直接解放节点malloc创建的空间就好,使用free,然后指针变成NULL

第三种:链表有多个节点。先通过循环寻找尾节点和尾节点前一个节点,然后释放最后的节点,将尾节点前的节点的next指针变成NULL就可以

2.3.6 头删

//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}

头删跟尾删刚开始差不多,但更为简单

2.3.7 查找

函数实现:

//查找
SLTNode* SLTFind(SLTNode* phead, SLDataType x)
{SLTNode* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

add.c文件中的函数使用: 

	SLTNode* p = SLTFind(node1, 3);if (p==NULL){printf("没找到\n");}else{printf("找到了\n");}//打印SLTPrint(node1);

2.3.8 在指定位置之前插入数据

函数实现:

//在指定位置之前插⼊数据 
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLDataType x)
{assert(pphead && *pphead);assert(pos);SLTNode* newnode = SLTBuyNode(x);//若pos==*pphead//头插if (pos == *pphead){SLTPushFront(pphead, x);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}newnode->next = pos;prev->next = newnode;}
}

函数运用:

SLTNode* plist = NULL;
SLTPushBack(&plist, 1);
SLTPushBack(&plist, 2);
SLTPushBack(&plist, 3);
SLTPushBack(&plist, 4);
SLTNode* find = SLTFind(plist, 3);
SLTInsert(& plist, find, 11);
SLTPrint(plist)

2.3.9 在指定位置之后插入数据

函数实现:

//在指定位置之后插?数据 
void SLTInsertAfter(SLTNode* pos, SLDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);newnode->next = pos->next;pos->next = newnode;
}

2.3.10. 删除pos节点

函数实现:

//删除pos节点 
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead);assert(pos);if (pos == *pphead)//pos等于*pphead,也就是说pos是头节点{//头删SLTPopFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}

分两种情况:头删和正常删除

2.3.11. 删除pos之后的节点

函数实现:

//删除pos之后的节点 
void SLTEraseAfter(SLTNode* pos)
{assert(pos&&pos->next);SLTNode* del = pos->next;pos->next = del->next;free(pos->next);del = NULL;
}

2.3.12 链表的销毁

函数实现:

//销毁链表 
void SListDesTroy(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur =next;}*pphead = NULL;
}

相关文章:

  • Netmiko 源码解析
  • openEuler对比CentOS的核心优势分析
  • 论文阅读:2025 arxiv Reward Shaping to Mitigate Reward Hacking in RLHF
  • Android学习总结之Retrofit篇
  • 生成器(generator)
  • 从新手到高手:小程序开发进阶技巧分享
  • 搭建spark-local模式
  • 《USB技术应用与开发》第四讲:实现USB鼠标
  • RabbitMQ安装流程(Windows环境)
  • 矩阵系统私信功能开发技术实践,支持OEM
  • 传统TDs系统。
  • CentOS7 部署 Ollama 全栈指南:构建安全远程大模型服务
  • Eigen线性代数求解器(分解类)
  • 代码随想录算法训练营Day31 | 56. 合并区间 738.单调递增的数字
  • 代码随想录算法训练营第二十七天(补)
  • ABAP Object Services
  • 通过gap看margin和padding在布局中的应用
  • 多个请求并行改造
  • 人工智能:入门阶段学习路径
  • 使用Xshell中自带的传输新建文件功能实现上传下载文件
  • 精准滴灌“种企业”,苏南强县常熟新的进阶密码
  • 韩国首尔江南区一大型商场起火,消防部门正在灭火中
  • 书信里的宋人|忠奸难辨的夏竦
  • 神舟二十号主要目的发布,在空间站驻留约6个月
  • 《国语汇校集注》:以1900余条注解,揭示隐微,提供思考
  • 国际货币基金组织:将今年美国经济增长预期下调0.9个百分点至1.8%