【数据结构与算法】单链表(无头单向非循环)
文章目录
- 1. 概念
- 2. 链表分类
- 3. 链表与顺序表对比
- 4. 无头单向非循环链表实现(C语言)
- 4.1 SingleLinkedList.h
- 4.2 Test.c
- 4.3 SingleLinkedList.c
1. 概念
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

链表在逻辑上是连续的,物理上则不一定连续(因为每个节点内存由操作系统分配),节点一般从堆内存申请,堆内存空间是按照一定策略分配的,两次申请的空间可能连续,也可能不连续。
2. 链表分类
以下不同情况下组合起来有8种链表结构:
- 单向或双向;
- 带头或不带头;
- 循环或非循环;
不过实际中最常用还是两种结构:
- 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等,另外这种结构在笔试面试中出现很多。

- 带头双向循环链表::结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,不同功能的实现反而简单了。

3. 链表与顺序表对比
| 不同角度 | 顺序表 | 链表 |
|---|---|---|
| 存储结构 | 逻辑、物理连续 | 逻辑连续,物理不一定连续 |
| 随机访问 | 支持,效率高 | 不支持,效率较低 |
| 插入或删除 | 效率低 | 效率高 |
| 容量 | 容量不足时需要扩容 | 不需要扩容 |
| 缓存利用率 | 高 | 低 |
| 应用场景 | 高效存储和频繁访问 | 频繁插入和删除 |
4. 无头单向非循环链表实现(C语言)
4.1 SingleLinkedList.h
#pragma once#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>// 单链表结构(无哨兵位)
typedef int datatype;
typedef struct SingleLinkedListNode {datatype val;struct SingleLinkedListNode* next;
} Node, * SingleLinkedList;// malloc返回新节点
Node* CreateNode(datatype val);// 头插尾插
void AddFront(Node** pphead, datatype val);
void AddBack(Node** pphead, datatype val);void Print(Node* phead);// 头删尾删
void RemoveFront(Node** pphead);
void RemoveBack(Node** pphead);// 查找是否存在
bool IsExist(Node** pphead, datatype target);
// 目标节点前面插入
void Insert(Node** pphead, datatype val, datatype target);
// 删除节点
void Erase(Node** pphead, datatype target);
// 删除全部
void Destroy(Node** pphead);// 查找并返回节点
Node* Find(Node** pphead, datatype target);
// 目标节点后面插入
void InsertAfter(Node* targetNode, datatype val);
// 删除目标节点后面的节点
void EraseAfter(Node* targetNode);
4.2 Test.c
#define _CRT_SECURE_NO_WARNINGS 1#include "SingleLinkedList.h"void TestAddFront() {SingleLinkedList linkedList = NULL;AddFront(&linkedList, 1);AddFront(&linkedList, 2);AddFront(&linkedList, 3);AddFront(&linkedList, 4);printf("\nTestAddFront(): ");Print(linkedList);
}void TestAddBack() {SingleLinkedList linkedList = NULL;AddBack(&linkedList, 1);AddBack(&linkedList, 2);AddBack(&linkedList, 3);AddBack(&linkedList, 4);printf("\nTestAddBack(): ");Print(linkedList);
}void TestRemoveBack() {SingleLinkedList linkedList = NULL;AddBack(&linkedList, 1);AddBack(&linkedList, 2);AddBack(&linkedList, 3);AddBack(&linkedList, 4);RemoveBack(&linkedList);printf("\nTestRemoveBack(): ");Print(linkedList);
}void TestTestFront() {SingleLinkedList linkedList = NULL;AddFront(&linkedList, 1);AddFront(&linkedList, 2);AddFront(&linkedList, 3);AddFront(&linkedList, 4);RemoveFront(&linkedList);printf("\nTestTestFront(): ");Print(linkedList);
}void TestInsert() {SingleLinkedList linkedList = NULL;Insert(&linkedList, 10, 11); // 测试空链表时Insert(&linkedList, 20, 10); // 测试空链表或节点数<=1时Insert(&linkedList, 30, 11); // 测试目标节点不存在时Insert(&linkedList, 40, 10); // 测试正常情况Insert(&linkedList, 1, 20); // 测试目标节点是头结点时printf("\nTestInsert(): ");Print(linkedList); // 1->20->40->10->30->NULL
}void TestErase() {SingleLinkedList linkedList = NULL;Insert(&linkedList, 10, 11); Insert(&linkedList, 20, 10); Insert(&linkedList, 30, 11); Insert(&linkedList, 40, 10); Insert(&linkedList, 1, 20); Erase(&linkedList, 30);printf("\nTestDelete(): ");Print(linkedList); // 1->20->40->10->NULL
}void TestDestroy() {SingleLinkedList linkedList = NULL;Insert(&linkedList, 10, 11);Insert(&linkedList, 20, 10);Insert(&linkedList, 30, 11);Insert(&linkedList, 40, 10);Insert(&linkedList, 1, 20);Destroy(&linkedList);printf("\nTestDestroy(): ");Print(linkedList);
}void TestInsertAfter() {SingleLinkedList linkedList = NULL;Insert(&linkedList, 10, 11);Insert(&linkedList, 20, 10);Insert(&linkedList, 30, 11);Insert(&linkedList, 40, 10);Insert(&linkedList, 1, 20);InsertAfter(Find(&linkedList, 30), 50); // 1->20->40->10->30->50->NULLInsertAfter(Find(&linkedList, 50), 100); // 1->20->40->10->30->50->100->NULLInsertAfter(Find(&linkedList, 1), 1000); // 1->1000->20->40->10->30->50->100->NULLprintf("\nTestInsertAfter(): ");Print(linkedList);
}void TestEraseAfter() {SingleLinkedList linkedList = NULL;Insert(&linkedList, 10, 11);Insert(&linkedList, 20, 10);Insert(&linkedList, 30, 11);Insert(&linkedList, 40, 10);Insert(&linkedList, 1, 20);InsertAfter(Find(&linkedList, 30), 50);InsertAfter(Find(&linkedList, 50), 100);InsertAfter(Find(&linkedList, 1), 1000); // 1->1000->20->40->10->30->50->100->NULLEraseAfter(Find(&linkedList, 1)); // 1->20->40->10->30->50->100->NULLEraseAfter(Find(&linkedList, 100)); // 1->20->40->10->30->50->100->NULLEraseAfter(Find(&linkedList, 10)); // 1->20->40->10->50->100->NULLprintf("\nTestEraseAfter(): ");Print(linkedList);
}int main() {TestAddFront();TestAddBack();TestRemoveBack();TestTestFront();TestInsert();TestErase();TestDestroy();TestInsertAfter();TestEraseAfter();return 0;
}
4.3 SingleLinkedList.c
#define _CRT_SECURE_NO_WARNINGS 1#include "SingleLinkedList.h"Node* CreateNode(datatype val) {Node* node = (Node*)malloc(sizeof(Node));if (node == NULL) {perror("CreateNode() malloc error");exit(-1);}node->val = val;node->next = NULL;return node;
}void AddFront(Node** pphead, datatype val) {Node* newNode = CreateNode(val);newNode->next = *pphead;*pphead = newNode;
}void AddBack(Node** pphead, datatype val) {Node* newNode = CreateNode(val);if (*pphead == NULL) { // 空链表*pphead = newNode;}else { /* 节点数>=1 */Node* tail = *pphead;while (tail->next){tail = tail->next;}tail->next = newNode;}
}void Print(Node* phead) {Node* cur = phead;while (cur != NULL) {printf("%d->", cur->val);cur = cur->next;}printf("NULL\n");
}void RemoveFront(Node** pphead) {assert(*pphead); // 空链表/* 链表节点数>=1 */Node* pNext = (*pphead)->next;free(*pphead);*pphead = pNext;//Node* tmp = *pphead;//*pphead = (*pphead)->next;//free(tmp);//tmp = NULL;
}void RemoveBack(Node** pphead) {assert(*pphead); // 空链表if ((*pphead)->next == NULL) { /* 只有1个节点 */free(*pphead);*pphead = NULL;}else { /* 节点数>=2 */Node* prev = *pphead;while (prev->next->next) {prev = prev->next;}free(prev->next);prev->next = NULL;}
}bool IsExist(Node** pphead, datatype target) {Node* cur = *pphead;while (cur) {if (cur->val == target) {return true;}cur = cur->next;}return false;
}void Insert(Node** pphead, datatype val, datatype target) {// 当 (1)空链表 或 (2)节点数<=1 或 (3)目标节点是头节点时 则直接头插if (*pphead == NULL || (*pphead)->next == NULL || (*pphead)->val == target) {AddFront(pphead, val);}else { // 节点数>=2if (IsExist(pphead, target)) {Node* prev = *pphead;while (prev->next->val != target) {prev = prev->next;}Node* targetNode = prev->next;Node* newNode = CreateNode(val);prev->next = newNode;newNode->next = targetNode;}else { // 当目标节点不存在时尾插AddBack(pphead, val);}}
}void Erase(Node** pphead, datatype target) {assert(*pphead); // 空链表Node* cur = *pphead;Node* pPrev = NULL; // 当节点数>=2必有pPrev != NULLwhile (cur) {if (cur->next != NULL && cur->next->val == target) {pPrev = cur;}if (cur->val == target) {Node* pNext = cur->next;free(cur);cur = NULL;if (pPrev != NULL) {pPrev->next = pNext;}else { // 说明删除的是头结点,pNext=NULL。*pphead = pNext;}break;}cur = cur->next;}
}void Destroy(Node** pphead) {Node* cur = *pphead;Node* del = NULL;while (cur) {del = cur;cur = cur->next;free(del);del = NULL;}*pphead = NULL;
}Node* Find(Node** pphead, datatype target) {Node* cur = *pphead;while (cur) {if (cur->val == target) {return cur;}cur = cur->next;}return NULL;
}void InsertAfter(Node* targetNode, datatype val) {assert(targetNode);Node* newNode = CreateNode(val);Node* pNext = targetNode->next;targetNode->next = newNode;newNode->next = pNext;
}void EraseAfter(Node* targetNode) {assert(targetNode);Node* pDel = targetNode->next;if (pDel != NULL) { // 避免targetNode是尾结点时pDel=NULL的情况Node* pNext = pDel->next;free(pDel);pDel = NULL;targetNode->next = pNext;}
}
相关文章:
【数据结构与算法】单链表(无头单向非循环)
文章目录 1. 概念2. 链表分类3. 链表与顺序表对比4. 无头单向非循环链表实现(C语言)4.1 SingleLinkedList.h4.2 Test.c4.3 SingleLinkedList.c 1. 概念 链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指…...
C#PDF转Excel
組件 Spire.Pdf.dll, v7.8.9.0 【注意:版本太低的没有此功能】 在Visual Studio中找到参考,鼠标右键点击“引用”,“添加引用”,将本地路径debug文件夹下的dll文件添加引用至程序。 界面图: 1个label,1…...
vivado xsim 终端 模拟
只模拟的话直接终端运行会快很多 计数器举例 mkdir srccounter.v module counter(input wire clk,input wire rst_n,output reg[31:0] cnt ); always (posedge clk or negedge rst_n)if(!rst_n)cnt < 31h0;elsecnt < cnt1;endmodule tb.v module tb; wire[31:0] out…...
Java并查集设计以及路径压缩实现
Java全能学习面试指南:https://javaxiaobear.cn 并查集是一种树型的数据结构 ,并查集可以高效地进行如下操作: 查询元素p和元素q是否属于同一组合并元素p和元素q所在的组 1、并查集的结构 并查集也是一种树型结构,但这棵树跟我们之…...
【leetcode】力扣算法之删除链表中倒数第n个节点【中等难度】
删除链表中倒数第n个节点 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 用例 输入:head [1,2,3,4,5], n 2 输出:[1,2,3,5] 输入:head [1], n 1 输出:[] 输入:head …...
C51--摇头测距小车
摇头测距小车——舵机和超声波封装 #include "reg52.h"#include "HC04.h" #include "Delay.h" #include "sg90.h" #include "motor.h"#define MIDDLE 0 #define LEFT 1 #define RIGHT 2void main() {char dir;double di…...
vue中slot和template用法传值
1 父页面调用assets-trend子组件,并接受assets-trend子组件传来的参数 <assets-trend style"flex: 2.7"><template slot-scope"slot">{{slot.slotMsg}}</template></assets-trend>2 子页面assets-trend使用slot传值 &…...
SQL性能分析-整理
昨日对MySQL的索引整理了一份小文档,对结构/分类/语法等做了一个小总结,具体文章可点击:MySQL-索引回顾,索引知识固然很重要,但引入运用到实际工作中更重要。 参考之前的文章:SQL优化总结以及参考百度/CSDN…...
常用计算电磁学算法特性与电磁软件分析
常用计算电磁学算法特性与电磁软件分析 参考网站: 计算电磁学三大数值算法FDTD、FEM、MOM ADS、HFSS、CST 优缺点和应用范围详细教程 ## 基于时域有限差分法的FDTD的计算电磁学算法(含Matlab代码)-框架介绍 参考书籍:The finite…...
PLC数组队列搜索FC(SCL代码+梯形图程序)
根据输入数据搜索输入数据队列中和输入数据相同的数,函数返回其所在队列的位置。这里我们需要用到博途PLC的数组指针功能,有关数组指针的详细使用方法,可以参考下面文章: 博途PLC数组指针: https://rxxw-control.blog.csdn.net/article/details/134761364 区间搜索FC …...
NUS CS1101S:SICP JavaScript 描述:前言、序言和致谢
前言 原文:Foreword 译者:飞龙 协议:CC BY-NC-SA 4.0 我有幸在我还是学生的时候见到了了不起的 Alan Perlis,并和他交谈了几次。他和我共同深爱和尊重两种非常不同的编程语言:Lisp 和 APL。跟随他的脚步是一项艰巨的任…...
软件测试常见问题2
1.用jmeter怎么进行测试? 使用JMeter进行测试的步骤如下: 启动JMeter,右键点击测试计划,选择添加->Threads(Users)->线程组,在线程组下创建请求。在请求中添加HTTP请求信息头,右键点击HTTP请求&…...
WPF XAML(一)
一、XAML的含义 问:XAML的含义是什么?为什么WPF中会使用XAML?而不是别的? 答:在XAML是基于XML的格式,XML的优点在于设计目标是具有逻辑性易读而且简单内容也没有被压缩。 其中需要提一下XAML文件在 Visu…...
每日一题:LeetCode-LCR 007. 三数之和
每日一题系列(day 18) 前言: 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🔎…...
四元数傅里叶变换(Quaternion Fourier Transforms) 在信号和图像处理中的应用
引言: 信号和图像处理是现代科学和工程领域中非常重要的一个方向,它涉及到对信号和图像进行分析、压缩、增强和恢复等操作。传统的信号和图像处理方法主要依赖于傅里叶变换和滤波器等工具,但这些方法在处理复杂系统时存在一定的局限性。近年来,四元数傅里叶变换作为一种新兴…...
vue项目之.env文件.env.dev、test、pro
.env文件是vue运行项目时的环境配置文件。 .env: 全局默认配置文件,所有环境(开发、测试、生产等)均会加载并合并该文件 .env.development(开发环境默认命名) 开发环境的配置,文件名默认为.env.development,如果需要改名也是可以的…...
Fabric2.2:在有系统通道的情况下搭建应用通道
写在最前 在使用Fabric-SDK-Go1.0.0操作Fabric网络时遇到了bug。Fabric-SDK-GO的当前版本没有办法在没有系统通道的情况下创建应用通道,而Fabric的最新几个版本允许在没有系统通道的情况下搭建应用通道。为了解决这个矛盾并使用Fabric-SDK-GO完成后续的项目开发&…...
测试人员必备基本功(2)
容易被忽视的bug 第二章 修改表单容易被忽视的bug 文章目录 容易被忽视的bug第二章 修改表单容易被忽视的bug 前言一、修改表单二、具体功能1.修改角色2.接口设计 三、测试设计1.测试点2.容易发现bug的测试点如下: 总结 前言 一个WEB系统的所有功能模块࿰…...
第十二章 Java内存模型与线程(一)
文章目录 12.3 Java内存模型12.3.1 主内存与工作内存12.3.2 内存间交互操作小结12.3.3 对于volatile型变量的特殊规则12.3.5 原子性、可见性与有序性12.3.6 先行发生原则 12.3 Java内存模型 12.3.1 主内存与工作内存 1.Java 内存模型规定了所有的变量都存储在主内存ÿ…...
C# WPF 数据绑定
需求 后台变量发生改变,前端对应的相关属性值也发生改变 实现 接口 INotifyPropertyChanged 用于通知客户端(通常绑定客户端)属性值已更改。 示例 示例一 官方示例代码如下 using System; using System.Collections.Generic; using System.ComponentModel; using Sys…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
