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

【数据结构与算法】单链表(无头单向非循环)

文章目录

  • 1. 概念
  • 2. 链表分类
  • 3. 链表与顺序表对比
  • 4. 无头单向非循环链表实现(C语言)
    • 4.1 SingleLinkedList.h
    • 4.2 Test.c
    • 4.3 SingleLinkedList.c

1. 概念

  链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
在这里插入图片描述
链表在逻辑上是连续的,物理上则不一定连续(因为每个节点内存由操作系统分配),节点一般从堆内存申请,堆内存空间是按照一定策略分配的,两次申请的空间可能连续,也可能不连续。

2. 链表分类

以下不同情况下组合起来有8种链表结构:

  1. 单向或双向;
  2. 带头或不带头;
  3. 循环或非循环;

不过实际中最常用还是两种结构:

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

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. 无头单向非循环链表实现&#xff08;C语言&#xff09;4.1 SingleLinkedList.h4.2 Test.c4.3 SingleLinkedList.c 1. 概念 链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指…...

C#PDF转Excel

組件 Spire.Pdf.dll, v7.8.9.0 【注意&#xff1a;版本太低的没有此功能】 在Visual Studio中找到参考&#xff0c;鼠标右键点击“引用”&#xff0c;“添加引用”&#xff0c;将本地路径debug文件夹下的dll文件添加引用至程序。 界面图&#xff1a; 1个label&#xff0c;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全能学习面试指南&#xff1a;https://javaxiaobear.cn 并查集是一种树型的数据结构 &#xff0c;并查集可以高效地进行如下操作&#xff1a; 查询元素p和元素q是否属于同一组合并元素p和元素q所在的组 1、并查集的结构 并查集也是一种树型结构&#xff0c;但这棵树跟我们之…...

【leetcode】力扣算法之删除链表中倒数第n个节点【中等难度】

删除链表中倒数第n个节点 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 用例 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 输入&#xff1a;head [1], n 1 输出&#xff1a;[] 输入&#xff1a;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子组件&#xff0c;并接受assets-trend子组件传来的参数 <assets-trend style"flex: 2.7"><template slot-scope"slot">{{slot.slotMsg}}</template></assets-trend>2 子页面assets-trend使用slot传值 &…...

SQL性能分析-整理

昨日对MySQL的索引整理了一份小文档&#xff0c;对结构/分类/语法等做了一个小总结&#xff0c;具体文章可点击&#xff1a;MySQL-索引回顾&#xff0c;索引知识固然很重要&#xff0c;但引入运用到实际工作中更重要。 参考之前的文章&#xff1a;SQL优化总结以及参考百度/CSDN…...

常用计算电磁学算法特性与电磁软件分析

常用计算电磁学算法特性与电磁软件分析 参考网站&#xff1a; 计算电磁学三大数值算法FDTD、FEM、MOM ADS、HFSS、CST 优缺点和应用范围详细教程 ## 基于时域有限差分法的FDTD的计算电磁学算法&#xff08;含Matlab代码&#xff09;-框架介绍 参考书籍&#xff1a;The finite…...

PLC数组队列搜索FC(SCL代码+梯形图程序)

根据输入数据搜索输入数据队列中和输入数据相同的数,函数返回其所在队列的位置。这里我们需要用到博途PLC的数组指针功能,有关数组指针的详细使用方法,可以参考下面文章: 博途PLC数组指针: https://rxxw-control.blog.csdn.net/article/details/134761364 区间搜索FC …...

NUS CS1101S:SICP JavaScript 描述:前言、序言和致谢

前言 原文&#xff1a;Foreword 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 我有幸在我还是学生的时候见到了了不起的 Alan Perlis&#xff0c;并和他交谈了几次。他和我共同深爱和尊重两种非常不同的编程语言&#xff1a;Lisp 和 APL。跟随他的脚步是一项艰巨的任…...

软件测试常见问题2

1.用jmeter怎么进行测试&#xff1f; 使用JMeter进行测试的步骤如下&#xff1a; 启动JMeter&#xff0c;右键点击测试计划&#xff0c;选择添加->Threads(Users)->线程组&#xff0c;在线程组下创建请求。在请求中添加HTTP请求信息头&#xff0c;右键点击HTTP请求&…...

WPF XAML(一)

一、XAML的含义 问&#xff1a;XAML的含义是什么&#xff1f;为什么WPF中会使用XAML&#xff1f;而不是别的&#xff1f; 答&#xff1a;在XAML是基于XML的格式&#xff0c;XML的优点在于设计目标是具有逻辑性易读而且简单内容也没有被压缩。 其中需要提一下XAML文件在 Visu…...

每日一题:LeetCode-LCR 007. 三数之和

每日一题系列&#xff08;day 18&#xff09; 前言&#xff1a; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f50e…...

四元数傅里叶变换(Quaternion Fourier Transforms) 在信号和图像处理中的应用

引言: 信号和图像处理是现代科学和工程领域中非常重要的一个方向,它涉及到对信号和图像进行分析、压缩、增强和恢复等操作。传统的信号和图像处理方法主要依赖于傅里叶变换和滤波器等工具,但这些方法在处理复杂系统时存在一定的局限性。近年来,四元数傅里叶变换作为一种新兴…...

vue项目之.env文件.env.dev、test、pro

.env文件是vue运行项目时的环境配置文件。 .env: 全局默认配置文件&#xff0c;所有环境(开发、测试、生产等&#xff09;均会加载并合并该文件 .env.development(开发环境默认命名) 开发环境的配置&#xff0c;文件名默认为.env.development,如果需要改名也是可以的&#xf…...

Fabric2.2:在有系统通道的情况下搭建应用通道

写在最前 在使用Fabric-SDK-Go1.0.0操作Fabric网络时遇到了bug。Fabric-SDK-GO的当前版本没有办法在没有系统通道的情况下创建应用通道&#xff0c;而Fabric的最新几个版本允许在没有系统通道的情况下搭建应用通道。为了解决这个矛盾并使用Fabric-SDK-GO完成后续的项目开发&…...

测试人员必备基本功(2)

容易被忽视的bug 第二章 修改表单容易被忽视的bug 文章目录 容易被忽视的bug第二章 修改表单容易被忽视的bug 前言一、修改表单二、具体功能1.修改角色2.接口设计 三、测试设计1.测试点2.容易发现bug的测试点如下&#xff1a; 总结 前言 一个WEB系统的所有功能模块&#xff0…...

第十二章 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 内存模型规定了所有的变量都存储在主内存&#xff…...

C# WPF 数据绑定

需求 后台变量发生改变,前端对应的相关属性值也发生改变 实现 接口 INotifyPropertyChanged 用于通知客户端(通常绑定客户端)属性值已更改。 示例 示例一 官方示例代码如下 using System; using System.Collections.Generic; using System.ComponentModel; using Sys…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...