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

【数据结构】单链表:头部操作我很行,插入也不用增容!!!

单链表

在这里插入图片描述
 
 
在这里插入图片描述

文章目录

  • 单链表
    • 1.链表
      • 1.1链表的概念和结构
      • 1.2链表的分类
    • 2.单链表的模拟实现
      • 2.1单链表的打印
      • 2.2单链表的尾插
      • 2.3单链表的头插
      • 2.4单链表的尾删
      • 2.5单链表的头删
      • 2.6单链表的查找
      • 2.7单链表的中间插入(在结点前插入)
      • 2.8单链表的中间删除(删除该结点)
      • 2.9单链表的中间插入(在结点后插入)
      • 2.10单链表的中间删除(删除该结点的后继)
      • 2.11单链表的销毁
    • 3.单链表的优缺点
    • 4. 链表的经典OJ题目训练

 

1.链表

 

1.1链表的概念和结构

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

在这里插入图片描述在这里插入图片描述
 ​
     现实中            数据结构中
 

 

在这里插入图片描述

 

1.2链表的分类

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构2*2*2):

  1. 单向或者双向

    在这里插入图片描述

  2. 带头或者不带头(哨兵位的头结点)

    在这里插入图片描述

  3. 循环或者非循环

    在这里插入图片描述

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:

在这里插入图片描述

  1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。这种结构在笔试面试中出现很多。
  2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势

 

2.单链表的模拟实现

SList.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLTDateType;//单链表结点结构
typedef struct SLTNode
{SLTDateType data;struct SLTNode* next;
}SLTNode;//单链表打印
void SLTPrint(SLTNode* phead);//尾插
void SLTPushBack(SLTNode** pphead, SLTDateType x);
//头插
void SLTPushFront(SLTNode** pphead, SLTDateType x);//尾删
void SLTPopBack(SLTNode** pphead);
//头删
void SLTPopFront(SLTNode** pphead);//查找
SLTNode* SLTFind(SLTNode* phead, SLTDateType x);
//结点前插入
void SLTInsert(SLTNode** pphead,SLTNode* pos, SLTDateType x);
//删除该结点
void SLTErase(SLTNode** pphead, SLTNode* pos);//结点后插入
void SLTInsertAfter(SLTNode* pos, SLTDateType x);
//删除该结点的后继
void SLTEraseAfter(SLTNode* pos);
//单链表的销毁
void SLTDestroy(SLTNode** pphead);//free完所有结点,将phead置空

 

SList.c

💡ps:单链表是使用一个SLTNode*的指针plist(头指针)来维护的。

⚠注意1:

  • 由于要通过函数接口来实现单链表的增删查改(修改单链表),就要进行址传递,传入&plist

  • 对于不需要修改单链表的接口,直接传入plist即可

 

⚠注意2:

  • 由于单链表的初始化比较简单,我们在测试文件中(test.c)中,直接SLTNode* plist = NULL即可

  • 单链表的销毁并不是简单地对头指针进行free,而是要对每个结点进行free

 

2.1单链表的打印

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

💡ps:由于一个结点存着下一个结点的地址,所以我们可以通过cur = cur->next的方式,来找下一个结点,遍历下去。

 

2.2单链表的尾插

SLTNode* BuyNewnode(SLTDateType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail\n");return NULL;}else{newnode->data = x;newnode->next = NULL;}return newnode;
}//链表的插入操作是不需要对phead进行判空的(因为空链表,phead就是NULL)
//尾插
void SLTPushBack(SLTNode** pphead, SLTDateType x)
{assert(pphead);//防止使用者传参时,传的plist而不是&plist特殊情况,如果是空链表,找不到尾结点,所以直接将newnode作为phead//1.构造新结点SLTNode* newnode = BuyNewnode(x);//特殊情况:空链表if (*pphead == NULL){*pphead = newnode;return;}else//一般情况:非空链表{//2.找到尾结点SLTNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}//3.嫁接tail->next = newnode;}}

💡ps:对于单链表的尾部操作,需要先找到尾结点。时间复杂度:O(N)。

而且对于空链表的情况无法找到尾结点,需要特殊处理。

 

2.3单链表的头插

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

💡ps:头部操作只需要注意连接关系即可,时间复杂度为:O(1)

 

2.4单链表的尾删

//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead);assert(*pphead);//删除操作,链表不可为空//特殊情况,如果链表只有一个结点是找不到前驱的,要进行特殊处理SLTNode* tail = *pphead;if (tail->next == NULL)//1个结点的情况{free(tail);*pphead = NULL;}else//多个结点的情况{SLTNode* prev = NULL;//1.找到尾结点和尾结点的前驱while (tail->next != NULL){prev = tail;//每次去下一个,用prev记录下来tail = tail->next;}//2.删除尾结点,进行嫁接和NULLfree(tail);tail = NULL;prev->next = NULL;}}

💡ps:找尾,时间复杂度为O(N)

由于尾删需要找尾结点的前驱,而只有一个结点的链表无法找到尾结点前驱,需要进行特殊处理。

 

2.5单链表的头删

void SLTPopFront(SLTNode** pphead)
{assert(pphead);assert(*pphead);SLTNode* first = *pphead;*pphead = first->next;free(first);
}

💡ps:时间复杂度为O(N)

 

2.6单链表的查找

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

 

2.7单链表的中间插入(在结点前插入)

//结点前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{assert(pphead);assert(pos);SLTNode* newnode = BuyNewnode(x);//特殊情况//头插if (pos == *pphead){SLTPushFront(pphead, x);}SLTNode* prev = *pphead;//1.找pos的前驱while (prev->next != pos){prev = prev->next;}//2.嫁接prev->next = newnode;newnode->next = pos;
}

💡ps:由于需要找结点pos的前驱,时间复杂度O(N)

pos为首结点时,无法找到pos的前驱需要进行特殊处理(头插操作)

 

2.8单链表的中间删除(删除该结点)

//删除该结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(*pphead);assert(pos);//特殊情况//头删if (*pphead == pos){SLTPopFront(pphead);}SLTNode* prev = *pphead;//1.找前驱while (prev->next != pos){prev = prev->next;}//2.嫁接prev->next = pos->next;free(pos);
}

💡ps:由于需要找结点pos的前驱,时间复杂度O(N)

pos为首结点时,无法找到pos的前驱需要进行特殊处理(头删操作)

 

2.9单链表的中间插入(在结点后插入)

//结点后插入
void SLTInsertAfter(SLTNode* pos, SLTDateType x)
{assert(pos);SLTNode* newnode = BuyNewnode(x);newnode->next = pos->next;pos->next = newnode;
}

 

2.10单链表的中间删除(删除该结点的后继)

//删除该结点的后继
void SLTEraseAfter(SLTNode* pos)
{assert(pos);assert(pos->next);//不能尾删SLTNode* next = pos->next;pos->next = next->next;free(next);next = NULL;
}

 

2.11单链表的销毁

void SLTDestroy(SLTNode** pphead)//free完所有结点,将phead置空
{SLTNode* cur = *pphead;while (cur){SLTNode* next = cur->next;free(cur);cur = next;}*pphead = NULL;
}

 

3.单链表的优缺点

优点:
1. 头部操作,效率很高,时间复杂度为O(1)
2. 结点之间不是连续存储的,在进行插入操作时,无法考虑增容。

 

缺点:
1. 不支持随机访问,即使知道是第几个结点,也要进行遍历才能找到该结点。
2. 尾部操作要找结点,效率低下(如果使用一个直接来指向尾结点,也可实现O(1)的效率)
 
💡ps:对于空链表进行尾部操作时,需要进行特殊处理,这个时候我们可以构造一个哨兵位的头结点,使问题简化。

 

4. 链表的经典OJ题目训练

  1. 删除链表中等于给定值 val 的所有结点。 OJ链接

  2. 反转一个单链表。 OJ链接

  3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。OJ链接

  4. 输入一个链表,输出该链表中倒数第k个结点。 OJ链接

  5. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有结点组成的。OJ链接

  6. 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。OJ链接

  7. 链表的回文结构。OJ链接

  8. 输入两个链表,找出它们的第一个公共结点。OJ链接

  9. 给定一个链表,判断链表中是否有环。 OJ链接

  10. 给定一个链表,返回链表开始入环的第一个结点。 如果链表无环,则返回 NULL OJ链接

  11. 给定一个链表,每个结点包含一个额外增加的随机指针,该指针可以指向链表中的任何结点或空结点。

    要求返回这个链表的深度拷贝。OJ链接

  12. 其他 。ps:链表的题当前因为难度及知识面等等原因还不适合我们当前学习,以后大家自己下去以后 Leetcode OJ链接 + 牛客 OJ链接

相关文章:

【数据结构】单链表:头部操作我很行,插入也不用增容!!!

单链表 文章目录单链表1.链表1.1链表的概念和结构1.2链表的分类2.单链表的模拟实现2.1单链表的打印2.2单链表的尾插2.3单链表的头插2.4单链表的尾删2.5单链表的头删2.6单链表的查找2.7单链表的中间插入(在结点前插入)2.8单链表的中间删除(删除该结点)2.9单链表的中间插入(在结点…...

SpringBoot——使用WebSocket功能

springboot自带websocket&#xff0c;通过几个简单的注解就可以实现websocket的功能&#xff1b; 启动类跟普通的springboot一样&#xff1a; /*** 2023年3月2日下午4:16:57*/ package testspringboot.test7websocket;import org.springframework.boot.SpringApplication; im…...

博弈论小课堂:非零和博弈(实现双赢)【纳什均衡点】

文章目录 引言I 非零和博弈1.1 囚徒问题1.2 博弈中双方的收益矩阵II 在现实中找均衡点2.1 博弈通常不是一次性的,而是反复进行的2.2 博弈论讲的都是阳谋的策略2.3 人类还处于文明的初级阶段,人的道德水准不容高估2.4 乌合之众效应2.5 很多时候看似是双赢,其实是在更大范围内…...

数组中的逆序对

解题思路1&#xff1a; 看到这个题目&#xff0c;我们的第一反应是顺序扫描整个数组。每扫描到一个数组的时候&#xff0c;逐个比较该数字和它后面的数字的大小。如果后面的数字比它小&#xff0c;则这两个数字就组成了一个逆序对。假设数组中含有n个数字。由于每个数字都要和…...

C++基础了解-01-基础语法

基础语法 一、基础语法 C 程序可以定义为对象的集合&#xff0c;这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象&#xff0c;方法、即时变量。 对象 - 对象具有状态和行为。例如&#xff1a;一只狗的状态 - 颜色、名称、品种&#xff0c;行为 -…...

phpmyadmin 文件包含(CVE-2014-8959)

0x01 漏洞介绍 phpMyAdmin是phpMyAdmin团队开发的一套免费的、基于Web的MySQL数据库管理工具。该工具能够创建和删除数据库,创建、删除、修改数据库表,执行SQL脚本命令等。phpMyAdmin的GIS编辑器中libraries/gis/GIS_Factory.class.php脚本存在目录遍历漏洞。远程攻击者可借助…...

SpringBoot集成MyBatis

目录 实现步骤 1. 在项目的 pom.xml 配置文件中引入如下依赖 2. 在项目的 application.properties 配置文件中添加如下依赖 3. 新建 UserMapper.class 接口类&#xff0c;添加如下 3 个方法 4. 在 /resources/mybatis/mapper 路径(需要手动创建文件夹)下创建 UserMapper.xm…...

MySQL-索引

索引介绍索引是对数据库表中一列或者多列的值进行排序的一种结构&#xff0c;使用索引可提高数据库中特定数据的查询速度。索引是一个单独的、存储在磁盘上的数据库结构&#xff0c;它们包含着对数据表里所有记录的引用指针。使用索引用于快速找出在某个或多个列中有一特定值得…...

【STM32存储器映射-寄存器基地址-偏移】

前言 在学习STM32的时候&#xff0c;我们看到很多的寄存器编程&#xff0c; 比方说LED灯&#xff1a; //GPIOB.5端口输出高电平GPIOB->ODR|1<<5; //PB.5 输出高GPIOE->ODR|1<<5; //PE.5输出高 //GPIOB端口全部输出高电平*(unsigned int*)(0x4001 …...

【华为OD机试2023】最多颜色的车辆 C++ Java Python

【华为OD机试2023】最多颜色的车辆 C++ Java Python 前言 如果您在准备华为的面试,期间有想了解的可以私信我,我会尽可能帮您解答,也可以给您一些建议! 本文解法非最优解(即非性能最优),不能保证通过率。 Tips1:机试为ACM 模式 你的代码需要处理输入输出,input/cin接收…...

特斯拉后端面试(部分)

HR告知如果面试通过要转.net-_- round1 有没有用过Java新版本&#xff0c;知道有哪些特性吗&#xff1f;A&#xff1a;没有。Q&#xff1a;我们基本在用JDK11&#xff0c;有的新项目用到JDK17了。参考答案1&#xff1a; ZGC: A Scalable Low-Latency Garbage Collector Epsi…...

【python】使用python将360个文件夹里的照片,全部复制到指定的文件夹中,并且按照顺序重新命名

最近要做一个图像生成的课题&#xff0c;在网上找了一个混合的数据集。这个数据集中一共有360个文件夹&#xff0c;然后文件夹中有6-9张不等的照片&#xff0c;我的目标就是编写python代码将所有的照片取出来&#xff0c;放到一个指定的文件夹里&#xff0c;并且从1开始按照顺序…...

【C语言】3天速刷C语言(初识)

【声明】本篇博客只用于对与刚学习C语言的同学的一个初始了解&#xff0c;具体内容请继续关注本专栏后续内容。什么是C语言C语言是一门通用计算机编程语言&#xff0c;广泛应用于底层开发。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及…...

如何搞定MySQL锁(全局锁、表级锁、行级锁)?这篇文章告诉你答案!太TMD详细了!!!

概述 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#xff08;CPU、RAM、I/O&#xff09;的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题&…...

云计算生态该怎么做?阿里云计算巢打了个样

2023 年 2 月 23 日至 24 日&#xff0c;由阿里云主办的「阿里云计算巢加速器」于杭州阿里云谷园区集结。 阿里云计算巢加速器于 2022 年 8 月正式启动招募&#xff0c;最终百奥利盟、极智嘉、EMQ、KodeRover、MemVerge 等 30 家创新企业入选计算加速器&#xff0c;覆盖了人工智…...

小樽C++ 多章⑧ (贰) 指针与数组

目录 1.C中数组变量名某些情况可以看成是指针 2.C语言的scanf 输入语句&#xff0c;printf 输出语句 3.用指针来当动态数组 小樽C 多章⑧ (壹) 指针变量https://blog.csdn.net/weixin_44775255/article/details/129031168 小樽C 多章⑧ (叁) 指针与字符串、(肆) 函数与指针…...

MXNet的机器翻译实践《编码器-解码器(seq2seq)和注意力机制》

机器翻译就是将一种语言翻译成另外一种语言&#xff0c;输入和输出的长度都是不定长的&#xff0c;所以这里会主要介绍两种应用&#xff0c;编码器-解码器以及注意力机制。编码器是用来分析输入序列&#xff0c;解码器用来生成输出序列。其中在训练时&#xff0c;我们会使用一些…...

RK3588平台开发系列讲解(同步与互斥篇)自旋锁介绍

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、自旋锁介绍二、自旋锁相关的函数1、普通场景2、进程上下文和下半部3、中断相关三、相关结构体四、函数实现1、初始化2、获取自旋锁沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇介绍自旋锁的使用和基…...

Linux系统CPU占用率较高问题排查思路

作为工程师&#xff0c;在日常工作中我们会遇到 Linux服务器上出现CPU负载达到100%居高不下的情况&#xff0c;如果CPU 持续跑高&#xff0c;则会影响业务系统的正常运行&#xff0c;带来企业损失。对于CPU过载问题通常使用以下两种方式即可快速定位&#xff1a;方法一第一步&a…...

源码解析——HashMap

源码解析——HashMap1. 什么 是HashMap2. 为什么要使用HashMap3. HashMap的使用4. 源码解析4.1 关键问题4.1 存储结构4.2 HashMap 的容量和负载因子4.3 初始化过程4.3 put() 方法实现原理4.3.1 hash()4.3.2 resize()4.4 get() 方法实现原理5. 面试题总结6. ConcurrentHashmap(J…...

【限时公开】20年农业AI工程师压箱底的17条精度校验铁律:从田间采集到模型上线零容错实践手册

第一章&#xff1a;农业图像识别精度校验的底层逻辑与行业特殊性农业图像识别并非通用计算机视觉任务的简单迁移&#xff0c;其精度校验需直面田间场景固有的复杂性&#xff1a;光照剧烈波动、作物生长阶段连续变化、病斑形态高度异质、背景杂草与土壤纹理干扰显著。这些因素共…...

【极限压测】从99.9%全红到5%安全线!2026最新横评5款硬核降AI工具

说真的&#xff0c;作为在知乎摸爬滚打好几年的博主&#xff0c;我太理解大家临近交稿时的那种绝望了。眼看着论文初稿要交&#xff0c;结果降ai检测一出来&#xff0c;竟然是红彤彤的99%&#xff1f;&#xff01;那一刻&#xff0c;我感觉脑袋真的“嗡”的一声。好不容易熬夜码…...

线程池:Java 并发编程的核心武器

线程池&#xff1a;Java 并发编程的"核心武器" 线程池是管理和复用线程的高级工具&#xff0c;它能显著提高程序性能&#xff0c;避免频繁创建和销毁线程的开销。 为什么需要线程池&#xff1f; 没有线程池的问题 // 传统方式&#xff1a;来一个任务创建一个线程 pub…...

Tailscale打洞失败太慢?手把手教你用Docker部署derper自建中转,告别国际绕行

Tailscale网络优化实战&#xff1a;用Docker自建derper中转节点提升连接速度 Tailscale作为现代零配置组网工具&#xff0c;其基于WireGuard协议的P2P直连特性确实令人惊艳——直到你发现两台设备之间的打洞成功率只有60%&#xff0c;而剩余40%的流量不得不绕行官方位于海外的中…...

如何突破分子观察瓶颈?PyMOL开源版的3大核心优势

如何突破分子观察瓶颈&#xff1f;PyMOL开源版的3大核心优势 【免费下载链接】pymol-open-source Open-source foundation of the user-sponsored PyMOL molecular visualization system. 项目地址: https://gitcode.com/gh_mirrors/py/pymol-open-source PyMOL开源版作…...

MATLAB图表美化指南:xlabel/ylabel上标下标的5种高级用法

MATLAB图表美化指南&#xff1a;xlabel/ylabel上标下标的5种高级用法 在数据可视化领域&#xff0c;MATLAB作为一款强大的科学计算软件&#xff0c;其图表绘制功能一直被科研人员和工程师广泛使用。然而&#xff0c;许多用户在基础绘图之外&#xff0c;往往忽略了坐标轴标签这一…...

Django 学习日记(补充1)| 彻底吃透:自定义 JWT 认证 + 全局登录中间件

大家好&#xff0c;这是我 Django 学习日记的第三篇。上一篇我们把路由、反向解析、DRF 自动路由、媒体文件、跨域全部讲明白了。今天我们进入整个项目最核心、最安全、最关键的部分&#xff1a;用户登录认证体系&#xff08;在进入视图前的一篇补充文章&#xff09;。本文将从…...

京东云GPU服务器省钱攻略:如何根据业务需求灵活选择计费模式和虚拟化方案

京东云GPU服务器成本优化实战指南&#xff1a;精准匹配业务需求的选型策略 在AI与高性能计算领域&#xff0c;GPU服务器已成为企业技术基础设施的核心组件。然而&#xff0c;面对复杂的计费模式、多样的硬件配置以及差异化的虚拟化方案&#xff0c;许多技术决策者常常陷入"…...

春联生成模型-中文-base多线程批量生成教程,为公司百名员工定制春节祝福

春联生成模型-中文-base多线程批量生成教程&#xff0c;为公司百名员工定制春节祝福 春节将至&#xff0c;为公司员工准备个性化春联是传递祝福的好方式。传统手工创作耗时耗力&#xff0c;而春联生成模型-中文-base结合多线程技术&#xff0c;能高效完成批量定制。本文将详细…...

RWKV7-1.5B-g1a部署案例:从零搭建轻量中文对话服务,60秒完成API调用

RWKV7-1.5B-g1a部署案例&#xff1a;从零搭建轻量中文对话服务&#xff0c;60秒完成API调用 1. 模型简介 rwkv7-1.5B-g1a是基于新一代RWKV-7架构开发的多语言文本生成模型&#xff0c;特别适合中文场景下的轻量级对话应用。这个1.5B参数的版本在保持较高生成质量的同时&#…...