【数据结构】链表(单链表与双链表实现+原理+源码)

news2024/9/22 21:19:59

博主介绍:✌全网粉丝喜爱+、前后端领域优质创作者、本质互联网精神、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战✌有需要可以联系作者我哦!

🍅附上相关C语言版源码讲解🍅

👇🏻 精彩专栏推荐订阅👇🏻 不然下次找不到哟

文章目录

    • 一、链表定义

      二、链表实战

      1、单链表(C语言实现版本)

      2、双链表(C++)

      三、分析总结

      优点:

      应用:

      小结

      大家点赞、收藏、关注、评论啦 !

      谢谢哦!如果不懂,欢迎大家下方讨论学习哦。

一、链表定义

链表是一种数据结构,它由一系列节点组成,这些节点按顺序连接在一起形成链式结构。每个节点包含数据和指向下一个节点的引用(指针)。链表的最后一个节点通常指向一个特定的值(如空值或null),表示链表的结束。

链表是一种数据结构,它由一系列节点组成,这些节点按顺序连接在一起形成链式结构。每个节点包含数据和指向下一个节点的引用(指针)。链表的最后一个节点通常指向一个特定的值(如空值或null),表示链表的结束。

链表可以分为单链表和双链表两种主要类型:
1. 单链表(Singly Linked List):每个节点包含数据和指向下一个节点的指针。链表的最后一个节点指向null。

节点1      节点2      节点3
| 数据1 | -> | 数据2 | -> | 数据3 | -> null

2. 双链表(Doubly Linked List):每个节点包含数据、指向下一个节点的指针,以及指向前一个节点的指针。这使得在双链表中可以更方便地进行前向和后向遍历。

null <- | 数据1 | <-> | 数据2 | <-> | 数据3 | -> null

链表优点:  链表相对于数组的优势在于插入和删除操作的效率较高,因为不需要移动大量元素,只需调整节点的指针。然而,链表的缺点是访问元素时需要按顺序遍历,而数组可以通过索引直接访问元素。链表在内存中不需要连续的存储空间,因此可以更灵活地分配内存。

二、链表实战

1、单链表(C语言实现版本)

#include <stdio.h>
#include <stdlib.h>

// 定义节点结构
struct Node {
    int data;           // 节点数据
    struct Node* next;  // 指向下一个节点的指针
};

// 定义链表结构
struct LinkedList {
    struct Node* head;   // 链表头指针
};

// 初始化链表
void initLinkedList(struct LinkedList* list) {
    list->head = NULL;  // 将头指针初始化为NULL,表示链表为空
}

// 在链表末尾添加节点
void append(struct LinkedList* list, int data) {
    // 创建新节点
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
    new_node->data = data;
    new_node->next = NULL;

    // 判断链表是否为空
    if (list->head == NULL) {
        // 如果为空,将新节点设为头节点
        list->head = new_node;
    } else {
        // 如果不为空,找到链表末尾,将新节点链接到末尾
        struct Node* current = list->head;
        while (current->next != NULL) {
            current = current->next;
        }
        current->next = new_node;
    }
}

// 在链表开头添加节点
void prepend(struct LinkedList* list, int data) {
    // 创建新节点
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
    new_node->data = data;
    new_node->next = list->head;

    // 将新节点设为头节点
    list->head = new_node;
}

// 删除节点
void deleteNode(struct LinkedList* list, int data) {
    struct Node* current = list->head;
    struct Node* prev = NULL;

    // 遍历链表,找到待删除节点及其前一个节点
    while (current != NULL && current->data != data) {
        prev = current;
        current = current->next;
    }

    // 如果找到待删除节点
    if (current != NULL) {
        // 如果待删除节点不是头节点
        if (prev != NULL) {
            prev->next = current->next;
        } else {
            // 如果待删除节点是头节点
            list->head = current->next;
        }
        free(current);  // 释放内存
    }
}

// 更新节点
void updateNode(struct LinkedList* list, int oldData, int newData) {
    struct Node* current = list->head;

    // 遍历链表,找到待更新节点
    while (current != NULL && current->data != oldData) {
        current = current->next;
    }

    // 如果找到待更新节点
    if (current != NULL) {
        current->data = newData;  // 更新节点数据
    }
}

// 搜索节点
struct Node* searchNode(struct LinkedList* list, int data) {
    struct Node* current = list->head;

    // 遍历链表,找到包含指定数据的节点
    while (current != NULL && current->data != data) {
        current = current->next;
    }

    return current;  // 返回节点指针
}

// 显示链表内容
void display(struct LinkedList* list) {
    struct Node* current = list->head;
    while (current != NULL) {
        printf("%d -> ", current->data);
        current = current->next;
    }
    printf("NULL\n");
}

// 释放链表内存
void freeLinkedList(struct LinkedList* list) {
    struct Node* current = list->head;
    struct Node* next = NULL;
    while (current != NULL) {
        next = current->next;
        free(current);
        current = next;
    }
    list->head = NULL;
}

int main() {
    struct LinkedList myLinkedList;
    initLinkedList(&myLinkedList);

    // 添加节点
    append(&myLinkedList, 1);
    append(&myLinkedList, 2);
    append(&myLinkedList, 3);

    // 显示链表内容
    printf("链表内容:");
    display(&myLinkedList);

    // 在开头添加节点
    prepend(&myLinkedList, 0);

    // 显示链表内容
    printf("在开头添加节点后的链表:");
    display(&myLinkedList);

    // 删除节点
    deleteNode(&myLinkedList, 2);

    // 显示链表内容
    printf("删除节点后的链表:");
    display(&myLinkedList);

    // 更新节点
    updateNode(&myLinkedList, 1, 10);

    // 显示链表内容
    printf("更新节点后的链表:");
    display(&myLinkedList);

    // 搜索节点
    int searchData = 10;
    struct Node* searchResult = searchNode(&myLinkedList, searchData);
    if (searchResult != NULL) {
        printf("找到数据为 %d 的节点。\n", searchData);
    } else {
        printf("未找到数据为 %d 的节点。\n", searchData);
    }

    // 释放链表内存
    freeLinkedList(&myLinkedList);

    return 0;
}

执行结果详细:

2、双链表(C++)

#include <iostream>  
#include <cstdlib>  
#include <cstdio>  
  
using namespace std;  
typedef struct Node  
{  
    int data;  
    struct Node *prior;  
    struct Node *next;  
} LinkList;  
  
LinkList *Create()  
{  
    LinkList *head;  
    head=(LinkList*)malloc(sizeof(LinkList));  
    if(head!=NULL)  
    {  
        head->prior=NULL;  
        head->next=NULL;  
        return head;  
    }  
    else return NULL;  
}  
  
int Insert(LinkList *head,int e)  //尾插法
{  
    LinkList *p;  
    LinkList *q=head;  
    p=(LinkList*)malloc(sizeof(LinkList));  
    if(p!=NULL)  
    {  
        p->data=e;  
        p->prior=NULL;  
        p->next=NULL;  
        while(q->next!=NULL)  
        {  
            q=q->next;  
        }  
        q->next=p;  
        return 1;  
    }  
    return 0;  
}  
  
LinkList* Change(LinkList *head) //变成双向链表后返回一个尾指针  
{  
    LinkList *p,*q;  
    p=head;  
    q=p->next;  
    while(q!=NULL)  
    {  
        q->prior=p;  
        p=p->next;  
        q=q->next;  
    }  
    return p;  
}  
  
void Output1(LinkList *head) //从头到尾遍历输出  
{  
    LinkList *p;  
    p=head->next;  
    while(p!=NULL)  
    {  
        printf("%d ",p->data);  
        p=p->next;  
    }  
    printf("\n");  
}  
  
void Output2(LinkList *tail) //从尾到头遍历输出  
{  
    LinkList *p;  
    p=tail;  
    while(p->prior!=NULL)  
    {  
        printf("%d ",p->data);  
        p=p->prior;  
    }  
    printf("\n");  
}  
  
void FreeLink(LinkList *head)  //释放
{  
    LinkList *p,*q;  
    p=head;  
    q=NULL;  
    while(p!=NULL)  
    {  
        q=p;  
        p=p->next;  
        free(q);  
    }  
}  
  
int main()  
{  
    LinkList *phead,*tail;  
    int n,e,flag;  
    phead=Create();  
    if(phead!=NULL)  
    {  
        cout<<"请输入长度:";
 scanf("%d",&n);  
        for(int i=0;i<n;i++)  
        {  
            scanf("%d",&e);  
            flag=Insert(phead,e);  
        }
 cout<<"从头到尾输出为: ";
        Output1(phead);  
        tail=Change(phead); 
 cout<<"从尾到头输出为: ";
        Output2(tail);  
        FreeLink(phead);  
    }  
    return 0;  
}

代码执行结果:

三、分析总结

链表是一种常见的数据结构,具有一些优点和应用:

优点:

1. 动态内存分配:链表允许在运行时动态分配内存,这使得它更加灵活,不需要预先指定存储空间大小,通过动态分配内存可以实现降低时间运行成本。

2. 插入和删除效率高:在链表中插入和删除节点相对容易且效率较高。相比之下,数组在中间或开头插入/删除元素可能需要移动大量元素。

3. 大小可变:*链表可以根据需要动态增长或缩小,而不浪费内存。

应用:

1. 实现动态数据结构:链表常用于实现其他动态数据结构,如栈、队列、图等。

2. 内存分配:动态链表的能力使其在动态内存分配的场景中非常有用,例如,动态分配内存的链表可用于管理操作系统的进程列表。

3. 实现算法:链表常用于算法实现,例如,链表在排序算法、图算法等方面有广泛的应用。

4. 嵌入式系统: 在资源受限的嵌入式系统中,链表可以更好地处理动态数据。

5. LRU缓存淘汰算法:链表可以用于实现LRU(Least Recently Used)缓存淘汰算法,用于管理缓存中的数据。

6. 数据库:数据库中的索引通常使用链表实现,以支持高效的插入和删除操作。

总的来说,链表在许多场景中都是一种强大且灵活的数据结构,特别适合那些需要频繁插入和删除操作的应用。

小结

大家点赞、收藏、关注、评论啦 !

谢谢哦!如果不懂,欢迎大家下方讨论学习哦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1404364.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【GoLang入门教程】Go语言工程结构详述

程序员裁员潮&#xff1a;技术变革下的职业危机 文章目录 程序员裁员潮&#xff1a;技术变革下的职业危机前言总结:专栏集锦强烈推荐写在最后 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网…

瑞金市城北社区开展新时代文明实践文艺汇演

为发扬中华民族优秀传统文化&#xff0c;促进社区居民邻里交流&#xff0c;丰富居民业余文化生活&#xff0c;1月18日&#xff0c;瑞金市城市社区城北社区新时代文明实践站在金盛小区开展新时代文明实践文艺汇演活动。 社区文艺爱好者们自编自演的节目丰富多彩&#xff0c;现场…

基于open3d的半径滤波

概念原理 半径滤波器比较简单粗暴。以某点为中心画一个圆计算落在该圆中点的数量&#xff0c;当数量大于给定值时&#xff0c;则保留该点&#xff0c;数量小于给定值则剔除该点。此算法运行速度快&#xff0c;依序迭代留下的点一定是最密集的&#xff0c;但是圆的半径和圆内点…

5.命令源码文件及命令行参数

目录 概述命令源码文件接收参数查看参数的使用说明结束 概述 命令源码文件接收参数 命令源码文件是程序的运行入口&#xff0c;是每个可独立运行的程序必须拥有的 无论是 Linux 还是 Windows&#xff0c;如果用过命令行&#xff08;command line&#xff09;的话&#xff0c;肯…

泥石流监测识别摄像机

泥石流监测识别摄像机是一种基于图像识别技术的监测设备&#xff0c;主要用于实时监测和识别泥石流的发生和演变过程&#xff0c;以预警和减灾为目的。这种摄像机通常采用高清晰度摄像头和图像处理系统&#xff0c;能够实时拍摄泥石流事件&#xff0c;并对图像进行处理和分析&a…

植物神经功能紊乱是什么?

植物神经也叫自律神经&#xff0c;它是一种自发的&#xff0c;非主观意识控制的&#xff0c;低级的神经活动。包括呼吸的、心律的、汗腺的、胃肠道的调节等等&#xff0c;都叫植物神经功能调节。 植物神经它的一旦出现了障碍可以有两种倾向&#xff0c;一种倾向就是出汗、兴奋…

Navicat使用HTTP通道连接远程服务器的SQLite文件

拷贝ntunnel_sqlite.php文件到Linux机器中 ntunnel_sqlite.php文件位置&#xff1a; 在Navicat安装位置中可以找到ntunnel_sqlite.php文件&#xff0c;其他两个类似文件是支持MySQL和pgsql的

【计算机网络】应用层——HTTP 协议(一)

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【网络编程】 本专栏旨在分享学习计算机网络的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一、什么是 HTTP 协…

MySQL-查询SQL语句

阅读目录 1.基础查询2.高级多表查询 回到顶部 1.基础查询 范围查询 在范围in select 字段 from 表名 where 字段 in (值,值); select id from stu where id in (2,3); 不在范围not in select 字段 from 表名 where 字段 in (值,值); select id from stu where id not in…

初识人工智能,一文读懂机器学习之逻辑回归知识文集(1)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

LLM:PI - 位置插值

标题:EXTENDING CONTEXT WINDOW OF LARGE LANGUAGE MODELS VIA POSITION INTERPOLATION 论文:https://arxiv.org/pdf/2306.15595.pdf 发表:2023 ​ Transformer的长度外推技术分为两类: 1:修改推理:比如PI、NTK-RoPE、YaRN、ReRoPE等。 优点:可直接修改推理模型,无…

Linux设备树中的 gpio 信息

一. 简介 前面几篇文章讲解了 pinctrl 子系统&#xff0c; pinctrl 子系统重点是设置 PIN( 有的 SOC 叫做 PAD) 的复用 和电气属性。 注意&#xff1a;如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话&#xff0c;那么接下来就要用到 gpio 子系统了。如果 PIN用作其他…

【Web前端开发基础】CSS的定位和装饰

CSS的定位和装饰 目录 CSS的定位和装饰一、学习目标二、文章内容2.1 定位2.1.1 定位的基本介绍2.1.2 定位的基本使用2.1.3 静态定位2.1.4 相对定位2.1.5 绝对定位2.1.6 子绝父相2.1.7 固定定位2.1.8元素的层级关系 2.2 装饰2.2.1 垂直对齐方式2.2.2 光标类型2.2.3 边框圆角2.2.…

Midjourney在线绘画及提示词精选库

网址:https://chat.xutongbao.top/ 一碗面粉&#xff1a; Self-Rising Flour in a 50s colourful bowl. professional photograph --ar 720:1170 --v 6 烟花古建筑&#xff1a; At night, with the snow-covered scenery of the Beijing Forbidden City as the backdrop, brill…

【Linux C | 进程】进程终止、等待 | exit、_exit、wait、waitpid

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

【网站项目】基于SSM的263货物进销管理系统

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

超融合基础架构理解

1 超融合基础架构 1.1 定义 超融合基础架构&#xff08;Hyper-converged infrastructure&#xff0c;缩写为HCI&#xff09;&#xff0c;是一种集成了存储设备及虚拟运算的信息基础架构框架。在这样的架构环境中&#xff0c;同一厂商的服务器与存储等硬件单元&#xff0c;搭配…

MySQL数据库查询语句之组函数,子查询语句

组函数 以组为操作单位&#xff0c;一组数据得到一个结果。 在没有手动分组的前提下&#xff0c;整张表默认为一组数据 max(列名)&#xff1a;获取最大值 min(列名)&#xff1a;获取最小值 sum(列名)&#xff1a;获取总和 avg(列名)&#xff1a;获取平均值 count(列名)&a…

R303 指纹识别模块功能实现流程

1 基本通信流程 1.1 UART 命令包的处理过程 1.2 UART 数据包的发送过程 UART 传输数据包前&#xff0c;首先要接收到传输数据包的指令包&#xff0c;做好传输准备后发送成功应答包&#xff0c;最后才开始传输数据包。数据包主要包括&#xff1a;包头、设备地址、包标识、包长…

画眉(京东科技设计稿转代码平台)介绍

前言 随着金融App业务的不断发展&#xff0c;为了满足不同场景下的用户体验及丰富的业务诉求&#xff0c;业务产品层面最直接体现就是大量新功能的上线及老业务的升级&#xff0c;随之也给研发带来了巨大的压力&#xff0c;所以研发效率的提升就是当前亟需解决的问题&#xff…