【数据结构与算法】深入浅出:单链表的实现和应用

news2024/10/7 7:32:01

 

🌱博客主页:青竹雾色间.

😘博客制作不易欢迎各位👍点赞+⭐收藏+➕关注

 ✨人生如寄,多忧何为 

目录

前言

单链表的基本概念

节点

头节点

尾节点

单链表的基本操作

创建单链表

头插法:

尾插法:

插入(增)操作

 删除(删)操作:

查找(查)操作:

修改(改)操作:

遍历链表

单链表的应用场景


前言

在本篇博客中,我们将深入探索一种常见的数据结构——单链表。单链表是一种线性数据结构,它由一系列节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。单链表的特点是插入和删除操作非常简单,但是查找和遍历操作可能会比较耗时。我们将学习单链表的基本概念、操作以及实现方式。

单链表的基本概念

下面我们来介绍一下单链表的基本概念和操作。

  • 节点

单链表中的每个节点都包含两个部分:数据域(DATA)和指针域(NEXT)。

数据域用于存储数据元素可以是数组,可以是int,甚至可以是结构体,

指针域用于存储指向下一个节点的指针

typedef int ElemType; //定义单链表结构
typedef struct Node{
    ElemType data;//数据域
    struct Node *next;//指针域
} LinkList;//初始化
  • 头节点

单链表的第一个节点称为头节点,它不包含任何数据元素,只包含一个指向第一个节点的指针。在单链表中,头节点通常被定义为全局变量或者静态变量。

//创建头结点,并将数据存入头结点中
LinkList CreateList(ElemType n){
    LinkList head = (LinkList)malloc(sizeof(struct Node));
    head->data = n;
    head->next = NULL;
    return head;
}
  • 尾节点

单链表的最后一个节点称为尾节点,它也不包含任何数据元素,只包含一个指向最后一个节点的指针。在单链表中,尾节点通常被定义为全局变量或者静态变量。

链表的尾节点NEXT指向NULL(空),因为尾部没有任何可以指向的空间了.

单链表的基本操作

单链表是一种常见的数据结构,支持以下四种基本操作:插入(增)、删除(删)、查找(查)、修改(改)。下面将逐一介绍这些操作的实现方法。

创建单链表

头插法:

我们首先创建一个头结点,然后将新节点插入到头结点的后面。具体实现时,我们可以使用指针来遍历链表,找到最后一个节点,然后将新节点插入到该节点的后面。这样就可以保证新节点始终位于链表的头部。

// 头插法
Node* insertAtHead(Node *head, int data) {
    Node *newNode = (Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->next = head;
    return newNode;
}

尾插法:

我们首先创建一个头结点,然后将新节点插入到头结点的后面。具体实现时,我们可以使用指针来遍历链表,找到最后一个节点,然后将新节点插入到该节点的后面。这样就可以保证新节点始终位于链表的尾部。

// 尾插法
Node* insertAtTail(Node *head, int data) {
    Node *newNode = (Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    if (head == NULL) { // 如果链表为空,则直接将新节点作为头结点。
        newNode->next = NULL;
        return newNode;
    } else if (head->next == NULL) { // 如果链表只有一个元素,则直接将新节点插入到该元素后面。
        head->next = newNode;
        return head;
    } else { // 否则,找到最后一个节点,然后将新节点插入到该节点的后面。
        Node *temp = head;
        while (temp->next != NULL) temp = temp->next;
        temp->next = newNode;
        return head;
    }
}

  • 插入(增)操作

          在单链表中插入一个新节点,将其链接到链表中的其他节点。

// 在指定位置插入一个新节点
void insertAtPos(Node** head, int pos, ElemType e) {
    Node* p = *head;
    int i = 1; // i表示当前节点的位置,从第二个节点开始计算
    while (i < pos && p != NULL) p = p->next, i++; // 从第二个节点开始遍历到指定位置的前一个节点
    Node* newNode = (Node*)malloc(sizeof(struct Node));
    if (newNode == NULL) exit(0); // 如果分配失败则退出程序
    newNode->data = e; // 将新节点的数据域设置为e
    if (p == NULL) *head = newNode; // 如果指定位置的前一个节点是空的,则将新节点作为新的头结点
    else newNode->next = p->next; // 否则将新节点插入到指定位置的前一个节点后面
    p->next = newNode; // 将新节点插入到链表中
}

  •  删除(删)操作

         从单链表中删除一个节点,重新连接链表中的其他节点。

  1. 若要删除的节点为头节点,直接将头节点指向下一个节点即可。
  2. 若要删除的节点不是头节点,遍历链表找到该节点的前一个节点。
  3. 将前一个节点的next指针指向要删除节点的下一个节点。

 

  • 查找(查)操作

        在单链表中查找特定的元素。

  1. 从头节点开始遍历链表,逐个比较节点的数据与目标数据是否相等。
  2. 若找到相等的节点,则返回该节点或其他需要的信息。
  3. 若遍历完整个链表仍未找到目标数据,则表示目标数据不存在于链表中。
//在单链表中查找值为x的结点
int Locate(LinkList L, int x)
{
    LinkList p;
    int j = 1;
    p = L->next;
    while (p != NULL && p->data != x)
    {
        p = p->next;
        j++;
    }
    if (p)
    {
        printf("%d在链表中,是第%d个元素", p->data - 48, j);//由于是ASCII,所以-48
    }
    else
    {
        printf("该数值不在链表里。\n");
        return 0;
    }
}

//求单链表的长度
int ListLength(LinkList L)
{
    Node* p;
    p = L->next;
    int j = 0;//计数器j
    while (p != NULL)
    {
        p = p->next;
        j++;
    }
    printf("%d", j);
    return 0;
}
  • 修改(改)操作

        更新单链表中节点的数据。

  1. 从头节点开始遍历链表,逐个比较节点的数据与目标数据是否相等。
  2. 若找到相等的节点,则将该节点的数据更新为新的数据。
//链表内容的修改,在链表中修改值为x的元素变为为k。
LinkedList LinkedListReplace(LinkedList L,int x,int k) {
    Node *p=L->next;
    int i=0;
    while(p){
        if(p->data==x){
            p->data=k;
        }
        p=p->next;
    }
    return L;
}
  • 遍历链表

在单链表中,遍历链表的操作可以通过以下步骤实现:

a. 从头结点开始遍历链表;

b. 对于每个节点,执行相应的操作(如打印数据元素)。

void Print(LinkList L) 
{
    Node* p = L->next;
    while (p) 
    {
        printf("%c ", p->data);
        p = p->next;
    }
}

单链表的应用场景

单链表在实际的软件开发中有广泛的应用,例如:

  • 数据库系统中的链表索引。

  • 实现栈和队列等其他数据结构。
  • 图算法中的邻接表表示。

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

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

相关文章

Shell脚本学习记录(常见指令)

Shell echo命令 Shell 的 echo 指令与 PHP 的 echo 指令类似&#xff0c;都是用于字符串的输出。命令格式&#xff1a; echo string 1.显示普通字符串: echo "It is a test" //双引号省略效果相同 2.显示转义字符 echo "\"It is a test\"" …

chatgpt赋能python:Python取出字典中键名对应的值

Python取出字典中键名对应的值 作为一个有10年Python编程经验的工程师&#xff0c;我经常遇到需要从字典中取出键名对应的值的情况。在这篇文章中&#xff0c;我将介绍Python中几种不同的方法来执行这个任务&#xff0c;并提供有关每个方法的优缺点的评估。让我们开始吧&#…

chatgpt赋能python:Python怎么取余数?

Python怎么取余数&#xff1f; 在Python中&#xff0c;我们经常需要进行数学运算&#xff0c;而其中计算余数也是经常用到的。虽然计算余数的方法似乎很简单&#xff0c;但是在不同的编程语言中有些微小的差异。本文将介绍在Python中如何高效地计算余数&#xff0c;以及一些相…

【动态规划】NK刷题记之DP6 连续子数组最大和(C语言实现)

【动态规划】NK刷题记之DP6 连续子数组最大和&#xff08;C语言实现&#xff09; 一、题目二、题解 2.1动态规划2.2贪心算法2.1.1 贪心算法的定义2.2.2贪心算法的性质2.2.3本题的贪心算法解决思路 2.2.4贪心与动态规划的区别 三、代码实现 3.1法一&#xff1a;动态规划(递归实…

无需公网IP,在家使用IPV6和电信光猫进行内网穿透以搭建远程主机

ipv4的公网IP弄起来还是比较麻烦&#xff0c;所以不管是搭建私人NAS还是远程登陆主机都总是需要进行内网穿透。一般的方案都是用花生壳这类的商用服务&#xff0c;然而这些服务一方面又贵又慢还有流量限制&#xff0c;另一方面还要进行把三代信息都盘出去的实名认证 1G到5G一个…

System V通信

文章目录 共享内存什么是共享内存&#xff08;物理内存块属性&#xff09;共享内存的接口认识查看共享内存删除共享内存共享内存的创建&#xff08;ftok和shmget&#xff09;挂接和去关联&#xff08;shmat和shmdt&#xff09; 利用共享内存通信&#xff08;简单的代码演示&…

Vue中如何进行数据缓存

Vue中如何进行数据缓存 Vue是一款流行的前端框架&#xff0c;它提供了许多方便的功能来处理数据。其中一个非常有用的功能是数据缓存。数据缓存可以提高应用程序的性能&#xff0c;减少网络请求&#xff0c;提高用户体验。在本文中&#xff0c;我们将介绍Vue中如何进行数据缓存…

chatgpt赋能python:Python如何取三位小数

Python 如何取三位小数 Python 是一种很强大的编程语言&#xff0c;可以应用于各个领域。其中&#xff0c;处理数字也是 Python 的一项强大功能。当我们需要对数字进行精细的操作时&#xff0c;常常需要使用到取小数的功能。本文将介绍如何使用 Python 取三位小数&#xff0c;…

Qgis中进行Shp和Excel属性连接实现百强县公共预算空间分析

前言 在之前的博文中&#xff0c;将2022的全国百强县一般公共预算收入的数据下载到了本地&#xff0c;博客原文地址&#xff1a;一种使用Java的快速将Web中表格转换成Excel的方法。对于不关注时空位置关系的一般分析&#xff0c;到此也就基本够用了。但是&#xff0c;如果站在全…

C语言函数初阶(1)

目录 1. 函数是什么 2. 库函数 3. 自定义函数 4. 函数参数 5. 函数调用 6. 函数的嵌套调用和链式访问 7. 函数的声明和定义 8. 函数递归 今天我们讲解前6个部分&#xff0c;下一个博客我们讲解后2个部分&#xff0c;因为后两个部分难度较大&#xff0c;讲解起来要花一点…

Vue中如何进行错误处理

Vue中如何进行错误处理 在Vue应用程序中&#xff0c;错误处理是必不可少的。错误可能发生在各种地方&#xff0c;例如网络请求、组件生命周期钩子函数、计算属性、方法等等。如果我们不正确地处理这些错误&#xff0c;可能会导致应用程序崩溃或无法正常工作。在本文中&#xf…

chatgpt赋能python:Python怎么反向切片

Python怎么反向切片 在Python中&#xff0c;切片是一种用于从序列中选取子序列的方法。正向切片从序列的第一个元素开始选取&#xff0c;而反向切片则从序列的最后一个元素开始选取。本文将介绍Python中如何使用反向切片。 什么是切片 在Python中&#xff0c;切片是一种操作…

IP协议的特性总结

目录 1. 地址管理 1.1 动态分配 1.2 NAT(网络地址转换)机制 1.3 IP地址的组成 1.4 IP地址网络号和主机号的划分 1.4.1 IP地址分类(ABCDE类) 1.4.2 子网掩码 1.5 特殊的IP地址 2. 路径规划 3. IP协议报文格式 3.1 分包 3.2 组包 1. 地址管理 IP地址在之前跟大家简单…

mfc读取obj格式文件初步

3dmax做一个box&#xff1b; 导出为cube1.obj&#xff1b; 记事本打开看一下该obj文件&#xff1b; # 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware # File Created: 10.06.2023 23:16:04mtllib cube1.mtl# # object Box001 #v -41.2323 0.0000 31.8849 v -4…

chatgpt赋能python:Python如何反向排序

Python如何反向排序 在Python中&#xff0c;排序是一项常见的任务。通常情况下&#xff0c;我们想对一组数据按照升序进行排序。但有时候&#xff0c;我们需要对这些数据进行反向排序&#xff0c;也就是按照降序进行排序。那么&#xff0c;Python该如何实现反向排序呢&#xf…

chatgpt赋能python:Python如何取出int内的个位数

Python如何取出int内的个位数 Python已经成为全球范围内最受欢迎的编程语言之一&#xff0c;它具有简单易学&#xff0c;可读性高和可扩展性等特点&#xff0c;因此它被广泛应用于数据科学、人工智能、网络编程、物联网和Web开发等领域。在Python编程中&#xff0c;有时需要从…

第七十天学习记录:高等数学:微分(宋浩板书)

微分的定义 基本微分公式与法则 复合函数的微分 微分的几何意义 微分在近似计算中应用 sin(xy) sin(x)cos(y) cos(x)sin(y)可以用三角形的几何图形来进行证明。 假设在一个单位圆上&#xff0c;点A(x,y)的坐标为(x,y)&#xff0c;点B(x’, y’)的坐标为(x’, y’)。则以两点…

44--Django-项目实战-全栈开发-基于django+drf+vue+elementUI企业级项目开发流程-支付宝二次封装、支付成功页面以及后台设计

一、支付宝支付介绍 需求:购买课程,付款 现在主流支付有支付宝支付、微信支持、银联支付 申请使用支付宝支付,需要有商户号(用户把钱付款到你的商户号中) 收手续费商户号要申请,需要有公司的营业执照(不需要营业执照也可以申请–》笔记)我们开发,需要商户号,公钥,…

Spring的数据访问哲学

目录 设计思路 了解Spring的数据访问异常体系 数据访问模板化 设计思路 Spring的目标之一就是允许我们在开发应用程序时&#xff0c;能够遵循面向对象(OO)原则中的“针对接口编程”Spring对数据访问的支持也不例外像很多应用程序一样&#xff0c;Spittr应用需要从某种类型的…

chatgpt赋能python:Python中使用Numpy获取数组元素的方法

Python中使用Numpy获取数组元素的方法 作为一种高级数据处理和科学计算库&#xff0c;numpy在python中被广泛使用。对于从事科研数据处理工作的工程师和研究人员来说&#xff0c;numpy已经成为必须要掌握的工具之一。 本文将讨论如何在Python中使用Numpy获取数组元素。我们将…