浅谈【数据结构】链表之单链表

目录

1、什么是数据?

2、什么是结构

3、什么是数据结构?

4、线性结构(线性表)

4.1线性表的物理结构的实现

5、链表

5.1无头结点的单链表

5.2新内容、老面孔

5.3数组和链表的优缺点

5.4链表的概念

5.5链表的创建步骤

5.5.1创建过程

5.5.2尾插法

5.5.3头插法

5.5.4中间插入法


谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注

没错,说的就是你,不用再怀疑!!!

希望我的文章内容能对你有帮助,一起努力吧!!!


在IT界有一个公式:程序=数据结构+算法

1、什么是数据?

数据(data)是客观事物的一个符号表示,在计算机科学中式指所有能够输入到计算机中并能够被计算 机程序处理的符号统称。

数据元素(data element)是数据的基本单位,在计算机程序中通常作为一个整体进行考虑和处理。一 个数据元素可以由若干个数据项(data item)组成。数据项是数据不可分割的最小单位。

数据对象(data object)是性质相同的数据元素的集合,是一个数据的子集。

2、什么是结构

结构:数据元素之间的关系的不同性质称之为:结构(structure)

根据数据元素之间的关系的性质不同, 通常有四类基本结构:

  • 集合:结构中的数据元素之间,除了“同属于一个集合的关系”之外,就没有其它关系了。
  • 线性结构:(顺序)
  • 树状结构:(层次)
  • 网状结构:(图状)

3、什么是数据结构?

数据结构的形式定义:数据结构是一个二元组

Data_Sturcture = (D,S)

其中:D是数据元素的有限集,S是D上的关系的有限集(数据元素之间的关系的集合)

结构定义中的“关系”描述的是数据元素之间的逻辑关系(指向关系),因此又称之为:“逻辑结构

逻辑结构”并不一定是数据元素在计算机中的存储(称为:映像)表示,数据元素存储结构称为:数据 的物理结构,又称:“存储结构”。

4、线性结构(线性表)

例子:

26个英文字母(A、B、C、D...Z)

 班上同学的学号

日期

...

线性表的数据元素可以是各种各样、但是同一个线性表中的元素必须要具有相同的特性,即同属于同一 个数据对象,相邻的元素之间存储序偶关系。

若将线性表记为:(a1、a2、a3....an)那么则会存在以下性质:

  • 存在唯一的一个被称为:"第一个"的数据元素
  • 存在唯一的一个被称为:“最后一个”的数据元素
  • 除了第一个元素之外,集合中的每一个数据元素均只有一个前驱结点
  • 除了最后一个元素之外,集合中的每一个数据元素均只有一个后继结点
    • 前驱结点:前面有结点
    • 后继结点:后面有结点

4.1线性表的物理结构的实现

在计算机中是怎么存储线性表的

  • 顺序结构
    • 线性表的顺序表示的是用一组地址连续的存储单元依次存储线性表中数据元素
      • 数组
      • 动态开辟内存空间
  • 链式结构
    • 链式结构存储用的存储单元的地址不一定连续的

5、链表

链表:链式存储的线性表

5.1无头结点的单链表

无头结点的单链表:没有头结点的单向的链式存储的线性表。

问题:我们在定义数组的时候,需要规定数组的长度,定义好了就不能修改了。

int a[10]; // 定义了一个元素为int类型的数组,数组大小为10个int类型

// 修改数组的大小

a = malloc(sizeof(int) * 100); // 不行的,因为a是常量指针,不能改变a指向。

链表就是有一个或者是多个结构体通过指针去进行指向关系构成的。

5.2新内容、老面孔

先来看一下下面这种结构体类型(带指针的结构体)

struct data

{

        int d_data; // 数据

        struct data *p_data; // 指针 存储数据元素的地址。在单链表结构里面,存储下一个数据元素的 内存地址

};

即想要按需分配,又可以进行灵活的删除和增加元素的时候:链表就可以解决这样的问题。

5.3数组和链表的优缺点

数组:

  • 优点:
    • 数组将元素按顺序存放在内存中,而且元素的内存占用都是相同的,因为它的地址连续、所有 可以通过下标快速的对数组元素进行访问。
    • 内存不会有太多的碎片化
  • 缺点:
    • 有可能造成内存资源的浪费
    • 数组元素的插入和删除比较复杂
    • 有可能内存资源不够用,容易造成段错误(内存越界)

链表:

  • 优点:
    • 插入和删除很方便
    • 可以按需分配,不会造成空间的浪费
  • 缺点:
    • 空间不是连续,访问相对数组而言效率要低很多碎片化比数组要高

获取结构体空间方式:

  • 动态内存开辟:堆空间的内存
    • 特性:空间的生存期是随进程持续性,需要手动释放
    • 保存空间地址
  • 通过定义变量:栈空间的内存
    • 特性:作用域结束,空间就会被系统释放
    • 用变量名

5.4链表的概念

链表就是由一个或者是多个包含指针成员变量的结构体,通过其指针变量来指向其他同类型结构体内存 地址来形成一种逻辑上的链式的数据结构。我们把每一个结构体实例(变量/空间)称为该链表的:结点 (node)

  • 首结点
    • 是链表中唯一一个没有前驱结点、只有后继结点的结点。是唯一一个指向其他的结点的结点
    • 首结点同时也是整个链表的首地址。
  • 尾结点
    • 是链表中唯一一个没有后继结点,只有前驱结点的结点。是唯一一个被其他结点指向的结点。

注意:尾结点的指针成员是指向 空 的

  • 可以保证尾结点的指针成员不会成为野指针
  • 可以通过判断指针是否指向空来判断是不是查询到了最后一个元素

***无头结点单链表基础示例***

/*无头结点的单链表
*/#include <iostream>/*结点类型
*/
struct node
{// 用来存储数据的空间(成员)称为:数据域int data;       // 用来保存其他结点的地址(关系)称为:指针域struct node *next;
};/*@brief:创建一个新链表@return:返回新链表的首结点的地址
*/
struct node *createNewList()
{// 新链表的首结点指针struct node *newList = nullptr;// 循环通过数据不断的去增加新结点到链表中while(1){int data = -1;// 通过键盘获取数据std::cin >> data;// 判断退出条件if(data == -1)break;// 申请了一个新结点的空间struct node *newNode = new struct node;newNode->data = data; // 将通过键盘获取到的数据存入结构体的数据域中newNode->next = nullptr; // 因为它是一个新结点,暂时是没有后继结点// 增加到链表里面:向后增加(尾插法)// 做第一次判断:链表中有没有结点if(newList == nullptr){// 如果newList是nullptr说明该链表里面为空,当前的新节点就是首届点newList = newNode;continue; // 继续增加新结点}// 搞一个临时指针,来指向首结点struct node *node_ptr = newList;// 找尾结点while(node_ptr->next)node_ptr = node_ptr->next;// 到这个位置 node_ptr 此时指向的结点是尾结点// 就可以把newNode作为尾结点的后继结点添加到链表里面去了node_ptr->next = newNode;}return newList;
}// 打印链表(遍历方法)
void printList(struct node *list)
{// 判断是不是空链表if(list == nullptr){std::cout << "链表为空" << std::endl;return;}std::cout << "List( ";// 如果不为空打印链表元素while(list) // 只要list不为空就一直循环{// list本来就可以表示首结点std::cout << list->data << " ";// 让list移动到下一个结点list = list->next;}std::cout << ")" << std::endl;
}int main()
{// 不在栈空间里面申请结点空间// 创建一个链表struct node *newList = createNewList();// 打印链表元素printList(newList); // 传入的值是newList存储的地址,并非newList自己的地址return 0;
}

5.5链表的创建步骤

  • 每获取一个数据,就一个创建一个结构体空间来存储数据
  • 把数据存放到结构体体空间中
  • 把这个新的结构体加入到链表中

5.5.1创建过程

  • 从无到有的过程:
    • 第一个结点诞生,此时首结点和尾结点都是第一个结点本身
  • 从少到多的过程
    • 在链表中增加结点,可以在链表尾部增加(尾插法)、也可以在链表的中间增加(中间插入 法)、也可以在链表头部增加(头插法)
  • 图示:

5.5.2尾插法

新节点增加在链表原尾结点的后面,即链表原尾结点要指向新结点(成为原尾结点的后继结点),然后 新结点就代替原尾结点成为新的尾结点

  • 特点:先加入的链表的结点在前面,而后加入结点在后面

5.5.3头插法

新结点在原首结点的前面,即新结点指向首结点(成为原首结点的前驱结点),然后新结点代替原首结 点称为新的首结点

  • 特点:先加入链表的结点在后面,后加入链表的结点在前面

5.5.4中间插入法

通过和每个结点数据进行比对,从而找到新结点存放的位置。通过中间插入法创建的链表是有序的。

  • 特点:链表的结点数据是有序的。

***单链表基础操作示例***

/*无头结点的单链表
*/#include <iostream>/*结点类型
*/
struct node
{// 用来存储数据的空间(成员)称为:数据域int data;       // 用来保存其他结点的地址(关系)称为:指针域struct node *next;
};/*@brief:头部插入@param: list 需要增加新数据的链表指针@param: data 是需要存入链表的数据@return : 链表首地址
*/
struct node* addNodeHead(struct node *list,int data)
{// list 表示就是一个链表(链表首地址/首结点的地址)头插法其实就是在它的首结点前面插入struct node *newNode = new struct node;newNode->data = data; // 数据存入结点数据域newNode->next = list; // 因为新结点作为新的首结点存入链表,那么原有的首结点就变成了新节点后继结点return newNode; // 返回newNode因为newNode变成了新的首结点了。更新链表的首地址
}/*@brief: 尾部插入@param: list 需要增加新数据的链表指针@param: data 是需要存入链表的数据@return : 链表首地址
*/
struct node* addNodeTail(struct node *list,int data)
{struct node *newNode = new struct node;newNode->data = data; // 将通过键盘获取到的数据存入结构体的数据域中newNode->next = nullptr; // 因为它是一个新结点,暂时是没有后继结点// 搞一个临时指针,来指向首结点struct node *node_ptr = list;// 找尾结点while(node_ptr->next)node_ptr = node_ptr->next;// 到这个位置 node_ptr 此时指向的结点是尾结点// 就可以把newNode作为尾结点的后继结点添加到链表里面去了node_ptr->next = newNode;return list;
}/*@brief : 中间插入法@param : list 需要增加数据的链表首地址@param : data 需要增加到链表的数据@return : 链表首地址
*/
struct node *addNodeMid(struct node *list,int data)
{struct node *newNode = new struct node;newNode->data = data; // 将通过键盘获取到的数据存入结构体的数据域中newNode->next = nullptr; // 因为它是一个新结点,暂时是没有后继结点// 循环比较数据 大到小  小到大// struct node *node_previce = nullptr;// struct node *node_current = list;// 第一种方法两个指针// while(node_current)// {//     if(node_current->data > data)//         break; // 找到插入位置了//     // 把两个指针往后移//     node_previce = node_current;//     node_current = node_current->next;// }// // 进行插入// if(node_previce == nullptr) // 说明需要作为首结点插入(头插入)// {//     // 头插代码// }// else// {//     // 直接插入到node_previce的后面//     newNode->next = node_previce->next; // 要成为node_previce下一个结点的前驱结点//     node_previce->next = newNode; // 然后让newNode成为node_previce的后继结点// }// 第二种方法提前判断struct node *node_ptr = list;// 如果第一个结点就比data大说明要插入到最前面(头插法)if(node_ptr->data > data){// 头插法list = addNodeHead(list,data);return list;} while(node_ptr&&node_ptr->next){std::cout << node_ptr->data << std::endl;if(node_ptr->data < data && node_ptr->next->data >= data) // 大于前一个小于后一个{// 将新结点的next指向当前结点的后继结点node_ptr->nextnewNode->next = node_ptr->next;// 让newNode成为node_ptr的后继结点node_ptr->next = newNode;return list; // 退出增加}// 把指针往后移node_ptr = node_ptr->next;}// 严谨判断一下node_ptr->next是不是为空if(node_ptr->next == nullptr) // 尾插法{node_ptr->next = newNode;}return list;
}/*@brief:创建一个新链表@return:返回新链表的首结点的地址
*/
struct node *createNewList()
{// 新链表的首结点指针struct node *newList = nullptr;// 循环通过数据不断的去增加新结点到链表中while(1){int data = -1;// 通过键盘获取数据std::cin >> data;// 判断退出条件if(data == -1)break;// 做第一次判断:链表中有没有结点if(newList == nullptr){struct node *newNode = new struct node;newNode->data = data; // 将通过键盘获取到的数据存入结构体的数据域中newNode->next = nullptr; // 因为它是一个新结点,暂时是没有后继结点// 如果newList是nullptr说明该链表里面为空,当前的新节点就是首届点newList = newNode;continue;}// 通过中间插入法增加结点newList = addNodeMid(newList,data);// 通过尾插法增加结点// newList = addNodeTail(newList,data);}return newList;
}// 打印链表(遍历方法)
void printList(struct node *list)
{// 判断是不是空链表if(list == nullptr){std::cout << "链表为空" << std::endl;return;}std::cout << "List( ";// 如果不为空打印链表元素while(list) // 只要list不为空就一直循环{// list本来就可以表示首结点std::cout << list->data << " ";// 让list移动到下一个结点list = list->next;}std::cout << ")" << std::endl;
}int main()
{// 不在栈空间里面申请结点空间// 创建一个链表struct node *newList = createNewList();// 打印链表元素printList(newList); // 传入的值是newList存储的地址,并非newList自己的地址// 删除元素int data = 0;std::cin >> data ;delNodeData(newList,data);// 打印链表元素printList(newList); // 传入的值是newList存储的地址,并非newList自己的地址return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

芯片后端之 PT 使用 report_timing 产生报告 之 -input_pins 选项

今天,我们再学习一点点 后仿真相关技能。 那就是,了解 report_timing 中的 -include_hierarchical_pins 选项。 如果我们仅仅使用如下命令,执行后会发现: pt_shell> report_timing -from FF1/CK -to FF2/d -delay_type max -include_hierarchical_pins 我们使用命…

【数据库】Mysql 批量变更所有字段类型为varchar的字符集

生成变更语句 SELECT CONCAT(ALTER TABLE , TABLE_NAME, MODIFY , COLUMN_NAME, , COLUMN_TYPE, , CHARACTER SET utf8 COLLATE utf8_general_ci , CASE WHEN IS_NULLABLE YES THEN NULL DEFAULT NULL WHEN IS_NULLABLE NO AND ISNULL(COLUMN_DEFAULT) THEN NOT NULL EL…

什么是持续集成(持续交付、部署)

文章目录 1 持续集成1.1 持续集成的好处1.2 持续集成的目的1.3 没有持续集成的状况 2 持续交付3 持续部署4 持续交付和持续部署的区别 1 持续集成 持续集成&#xff08;Continuous integration&#xff0c;简称CI&#xff09;&#xff0c;简单来说持续集成就是频繁地&#xff…

数字孪生网络 (DTN)关键技术分析

数字孪生网络 (DTN): 概念、架构及关键技术 摘要 随着5G商用规模部署和下一代互联网IPv6的深化应用&#xff0c;新一代网络技术的发展引发了产业界的广泛关注。智能化被认为是新一代网络发展的趋势&#xff0c;为数字化社会的信息传输提供了基础。面向数字化、智能化的新一代网…

【Linux篇】Linux命令基础

目录 1. Linux的目录结构 1.1 Linux的目录结构 1.2 /在Linux系统中的表示 2. linux命令基础 2.1 什么是命令和命令行 2.2 Linux命令的通用格式 2.3 ls命令 2.3.1 ls命令的参数的作用&#xff1a; 2.3.2 ls命令的选项 2.3.3 命令的选项组合使用 2.4 cd切换工作目录 2…

【微信小程序】使用 npm 包 - API Promise化-- miniprogram-api-promise

1. 基于回调函数的异步 API 的缺点 默认情况下&#xff0c;小程序官方提供的异步 API 都是基于回调函数实现的&#xff0c;例如&#xff0c;网络请求的 API 需要按照如下的方式调用&#xff1a; 缺点&#xff1a;容易造成回调地狱的问题&#xff0c;代码的可读性、维护性差&a…

HTML简单了解和基础知识记录

参考视频 html的用途 超文本标记语言&#xff08;英语&#xff1a;HyperText Markup Language&#xff0c;简称&#xff1a;HTML&#xff09;&#xff0c;用来显示网页的文字和框架结构&#xff0c;可以认为是网页的骨架。 标签/元素 用于定义文字图片连接等&#xff0c;分…

VLDB 2024 即将来袭!创邻科技将带来精彩分享

8月26-30日&#xff0c;数据库领域最权威、影响力最大的顶级盛会之一&#xff0c;VLDB 2024 来了&#xff01; VLDB&#xff08;International Conference on Very Large Databases&#xff09;是数据管理、可扩展数据科学和数据库研究人员、厂商、应用开发者以及用户广泛参与…

能实现可算不可见的同态加密技术详解

目录 同态加密的基本概念 同态加密示例 同态加密的原理 同态加密的类型 同态加密的应用场景 同态加密的挑战 小结 同态加密&#xff08;Homomorphic Encryption&#xff0c;HE&#xff09;是一种满足密文同态运算性质的加密算法&#xff0c;可以在加密数据上直接执行特定…

WiFi的IP和电脑IP一样吗?怎么更改wifi的ip地址

在数字化时代&#xff0c;网络连接已成为我们日常生活和工作中不可或缺的一部分。无论是通过手机、电脑还是其他智能设备接入互联网&#xff0c;IP地址作为网络设备的唯一标识&#xff0c;扮演着至关重要的角色。然而&#xff0c;很多用户对于WiFi的IP地址与电脑&#xff08;或…

3.4-CoroutineScope/CoroutineContext:coroutineScope() 和 supervisorScope()

文章目录 coroutineScope()supervisorScope()总结 coroutineScope() coroutineScope() 和我们创建协程时的 CoroutineScope 名字是相同的&#xff0c;实际上它们也确实有所关联&#xff0c;为了方便理解我们先说下 coroutineScope() 是怎样的效果。 我们在使用 coroutineScop…

计算机网络常见面试题总结

文章目录 1 计算机网络基础1.1 网络分层模型1.1.1 OSI 七层模型是什么&#xff1f;每一层的作用是什么&#xff1f;1.1.2 TCP/IP 四层模型是什么&#xff1f;每一层的作用是什么&#xff1f;1.1.3 为什么网络要分层&#xff1f; 1.2 常见网络协议1.2.1 应用层有哪些常见的协议&…

MySQL主从复制之GTID模式

目录 1 MySQL 主从复制 GTID 模式介绍 2 传统复制模式与GTID复制模式的区别 3 GTID模式核心参数 4 GTID 实现自动复制原理 4.1 GTID基本概念 4.2 GTID复制流程 5 GTID 实现自动定位 5.1 配置 my.cnf 5.2 配置 SLAVE 实现自动定位 5.3 测试 6 GTID 模式 故障转移的方法流程 6.1…

记一次NULL与空字符串导致的分组后产生重复数据

目录 一&#xff0c;场景说明二&#xff0c;实现功能三&#xff0c;修改原实现方法四&#xff0c;说明 一&#xff0c;场景说明 想实现这样一个功能&#xff0c;统计人员信息中不同性别的人的总工资。 实现方式&#xff1a;将数据group by 分组后累加。 二&#xff0c;实现功…

SpringMVC核心机制环境搭建

文章目录 1.SpringMVC执行流程1.基础流程图2.详细流程图 2.安装Tomcat1.下载2.解压到任意目录即可3.IDEA配置Tomcat1.配置Deloyment2.配置Server 3.创建maven项目1.创建sun-springmvc模块&#xff08;webapp&#xff09;2.查看是否被父模块管理3.pom.xml引入依赖4.目录5.SunDis…

npm阿里云制品仓库

配置 配置仓库地址&#xff0c;可以再在仓库指南看到 npm config set registryxxxxx#登录&#xff0c;帐户密码可以在仓库指南看到 npm login注意&#xff1a;npm>9的版本npm login目前有问题 verbose web login not supported, trying couch&#xff0c;暂时没试验到解决…

C语言函数递归

前言与概述 本文章将通过多个代码并赋予图示&#xff0c;详细讲解C语言函数递归的定义和函数递归的运算过程。 函数递归定义 程序调用自身的编程技巧称为递归。递归作为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法。它…

LLM agentic模式之规划能力(planning)

文章目录 任务分解分解优先方法交替分解方法 多计划选择外部规划器辅助规划反思和改进记忆增强规划评估 2024年2月的综述《 Understanding the planning of LLM agents: A survey》提供了基于LLM的的agent的规划(planning)能力的系统视角&#xff0c;总结了近年来提高规划能力…

EmguCV学习笔记 VB.Net 6.4 霍夫变换

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

基于x86 平台opencv的图像采集和seetaface6的人脸朝向姿态估计功能

目录 一、概述二、环境要求2.1 硬件环境2.2 软件环境三、开发流程3.1 编写测试3.2 配置资源文件3.2 验证功能一、概述 本文档是针对x86 平台opencv的图像采集和seetaface6的人脸朝向姿态估计功能,opencv通过摄像头采集视频图像,将采集的视频图像送给seetaface6的人脸朝向姿态…