数据结构(1)数据结构基础(单向链表)

一、什么是数据结构

        数据结构是一组用来保存一种或多种特定关系的数据的集合。其主要目的是组织和存储数据,以便于高效的访问和修改。在程序设计中,将大量而复杂的问题以特定的数据类型和特定的存储结构存储在内存中,并在此基础上实现某个特定的功能的操作,即:

                                程序 = 数据结构 + 算法

二、 数据与数据之间的关系

2.1 数据的逻辑结构
  • 集合:数据元素之间关系平等,常用于处理无关元素的组。
  • 线性结构:元素之间存在一对一的关系,常见的有数组、链表、队列、栈等。
  • 树型结构:元素之间存在一对多的关系,典型的如二叉树。
  • 图形结构:元素之间存在多对多的关系,形成网络状的结构。
2.2 数据的物理结构

数据的逻辑结构在计算机内存中的存储方式是其物理结构,主要包括以下几种存储方式:

  • 顺序存储
    • 利用一段连续的内存空间来保存元素。
    • 优点
      • 空间连续,访问方便(如随机访问)。
    • 缺点
      • 插入与删除操作需要移动大量元素。
      • 需要预分配内存空间。
      • 容易造成存储空间碎片。
  • 链式存储
    • 使用非连续的内存空间保存元素。
    • 优点
      • 插入和删除数据方便。
      • 不需要预分配内存。
    • 缺点
      • 访问元素的效率低。
  • 索引存储
    • 通过关键字构建索引表,通过该索引表找到数据的存储位置。
  • 散列存储(哈希存储)
    • 将数据元素的存储位置与关键码之间建立确定对应关系,以实现快速查找。

三、储备知识

  • 指针:用于存储其他变量的地址,是链式存储结构的关键。
  • 结构体:在编程中,常用结构体定义数据节点,便于管理数据。
  • 动态内存分配:为数据结构中的节点动态分配和释放内存。

四、 链式数据结构:单向链表

4.1 定义

        单向链表是一种线性数据结构,由一系列节点构成,每个节点包含数据部分和指向下一个节点的指针。它是一种灵活的存储方式,相比于数组,单向链表不需要连续的内存空间。

4.2 结构
  • 节点(Node)

    • 数据部分:存储实际的数据。
    • 指针部分:指向下一个节点的指针。
  • 链表头(Head)

    • 链表的起始节点,所有操作通常从此节点开始。
  • 尾节点(Tail)

    • 链表的最后一个节点,其指针部分为空,表示链表的结束。
4.3 主要操作
  • 插入操作

    • 在头部:创建新节点,将其指针指向当前头节点,然后更新头节点。
    • 在尾部:遍历找到最后节点,令其指向新节点,并将新节点的指针设为 null
    • 在指定位置:找到插入位置的前一节点,将新节点连接至该位置。
  • 删除操作

    • 删除头节点:更新头节点为当前头节点的下一个节点。
    • 删除尾节点:遍历找到倒数第二个节点,更新其指针为 null
    • 删除特定位置节点:找到前一节点,调整指针跳过待删除节点。
  • 查找操作

    • 从头节点开始遍历链表,逐个比较数据部分查找特定值。
4.4 优缺点
  • 优点
    • 动态存储,插入和删除方便。
  • 缺点
    • 存储开销大(每个节点额外的指针)。
    • 访问效率低(需要遍历)。

示例:

// 定义数据类型为整型  
typedef int Datatype;  // 定义链表节点结构  
typedef struct node  
{  Datatype data; // 节点存储的数据  struct node * pnext; // 指向下一个节点的指针  
} node_t;  // 定义链表结构  
typedef struct link  
{  struct node* phead; // 指向链表头节点的指针  int clen; // 链表中节点的数量  
} link_t;// 创建链表  
link_t* create_link()  
{  // 分配内存给链表结构  link_t * plink = malloc(sizeof(link_t));  if(NULL == plink )  {  perror("create link error"); // 内存分配失败  return NULL;  }  plink->phead = NULL; // 初始化头指针为空  plink->clen = 0; // 初始化链表长度为0  return plink; // 返回新创建的链表  
}  // 在链表头部插入新节点  
int push_into_link_head(link_t* plink, Datatype data)  
{  // 分配内存给新节点  node_t* pnode = malloc(sizeof(node_t));  if(NULL == pnode)  {  perror("push node error"); // 内存分配失败  return -1;  }  pnode->data = data; // 设置节点数据  pnode->pnext = plink->phead; // 新节点指向当前头节点  plink->phead = pnode; // 更新头节点为新节点  plink->clen++; // 增加链表长度  return 0; // 成功  
}  // 遍历并打印链表中的所有节点数据  
int traverse_print_link(link_t* plink)  
{  node_t* p = plink->phead; // 从头节点开始  while(p)  {  printf("%d ", p->data); // 打印节点数据  p = p->pnext; // 移动到下一个节点  }  printf("\n"); // 打印换行  return 0; // 成功  
}  // 在链表尾部插入新节点  
int push_into_link_tail(link_t * plink, Datatype data)  
{  // 分配内存给新节点  node_t * pnode = malloc(sizeof(node_t));  pnode->data = data; // 设置节点数据  pnode->pnext = NULL; // 新节点的下一个指针为空  if(plink->phead == NULL) // 如果链表为空  {  plink->phead =  pnode; // 头指针指向新节点  }  else  {  node_t* p = plink->phead; // 从头节点开始遍历  while(p->pnext) // 找到最后一个节点  {  p = p->pnext;  }  p->pnext = pnode; // 将最后一个节点的指针指向新节点  }  plink->clen++; // 增加链表长度  return 0; // 成功  
}  // 从链表头部删除节点  
int pop_into_link_head(link_t* plink)  
{  if(NULL == plink->phead) // 如果链表为空  {  return 0; // 无需删除  }  else  {  node_t *pnode = plink->phead; // 获取头节点  plink->phead = pnode->pnext; // 更新头节点为下一个节点  free(pnode); // 释放原头节点内存  }  plink->clen--; // 减少链表长度  return 0; // 成功  
}  // 从链表尾部删除节点  
int pop_into_link_tail(link_t* plink)  
{  if(NULL == plink->phead) // 如果链表为空  {  return 0; // 无需删除  }  else if(plink->phead->pnext == NULL) // 如果只有一个节点  {  pop_into_link_head(plink); // 删除头节点  }  else  {  node_t * pnode = plink->phead; // 从头节点开始遍历  while(pnode->pnext->pnext) // 找到倒数第二个节点  {  pnode = pnode->pnext;  }  free(pnode->pnext); // 释放最后一个节点内存  pnode->pnext = NULL; // 将倒数第二个节点的指针设为NULL  plink->clen--; // 减少链表长度  return 0; // 成功  }  return 0; // 成功  
}  // 在链表中查找特定数据的节点  
node_t * search(link_t* plink, Datatype data)  
{  node_t* pnode = plink->phead; // 从头节点开始  while(pnode)  {  if(pnode->data == data) // 如果找到数据  {  return pnode; // 返回该节点  }  pnode = pnode->pnext; // 移动到下一个节点  }  return NULL; // 未找到  
}  // 修改链表中指定数据的节点数据  
int change_link_data(link_t* plink, Datatype srcdata, Datatype destdata)  
{  node_t * node = search(plink, srcdata); // 查找源数据节点  node->data = destdata; // 修改节点数据  return 0; // 成功  
}  // 销毁链表,释放所有节点内存  
int destory_link(link_t* plink)  
{  while(plink->phead) // 当链表不为空  {  pop_into_link_head(plink); // 删除头节点  }  free(plink); // 释放链表结构内存  return 0; // 成功  
}  // 查找链表中的中间节点  
node_t * search_mid_node(link_t * plink)  
{  node_t * pnode = plink->phead; // 从头节点开始  if(NULL == pnode) // 如果链表为空  {  return NULL; // 返回NULL  }  if(plink->clen == 1) // 如果只有一个节点  {  return pnode; // 返回该节点  }  int i = 0;  while(pnode)  {  pnode = pnode->pnext; // 移动到下一个节点  i++;  if(i == (plink->clen) / 2) // 找到中间节点  {  break;  }  }  return pnode; // 返回中间节点  
}  // 从链表中查找倒数第number个节点  
node_t *search_countdown(link_t* plink, int number)  
{  node_t* pnode = plink->phead; // 从头节点开始  if(NULL == pnode) // 如果链表为空  {  return NULL; // 返回NULL  }  if(plink->clen == 1) // 如果只有一个节点  {  if(number == 1)  {  return pnode; // 返回该节点  }  return NULL; // 返回NULL  }  int i = 0;  while(pnode)  {  pnode = pnode->pnext; // 移动到下一个节点  i++;  if(i == (plink->clen) - number) // 找到倒数第number个节点  {  break;  }  }  return pnode; // 返回该节点  
}  // 删除指定位置的节点  
int pop_appointed_node(link_t* plink, int appointed)   
{  if (plink->phead == NULL) // 如果链表为空  {  return -1; // 返回错误  }  if (appointed < 1 || appointed > plink->clen) // 如果指定位置不合法  {  return -1;   }  node_t* pnode = plink->phead;    if (appointed == 1) // 如果删除的是头节点  {  pop_into_link_head(plink); // 删除头节点  return 0;  }  for (int i = 1; i < appointed - 1; i++) // 遍历到指定位置的前一个节点  {  pnode = pnode->pnext;   }  node_t* temp = pnode->pnext; // 获取要删除的节点  if (temp == NULL) // 如果要删除的节点不存在  {  return -1;  }  pnode->pnext = temp->pnext; // 将前一个节点的指针指向要删除节点的下一个节点  free(temp); // 释放要删除节点的内存  plink->clen--; // 减少链表长度  return 0; // 成功  
}

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

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

相关文章

Docker安装Neo4j图数据库和APOC插件

文章目录 一、前言二、安装Neo4j三、测试Neo4j四、安装APOC插件五、测试APOC插件 一、前言 官方文档&#xff1a;https://neo4j.com/docs/operations-manual/current/docker/introduction/ 二、安装Neo4j 我这里以 5.23.0 版的 Neo4j 为例 拉取镜像 docker pull neo4j:5.23.0…

Java-用户线程与守护线程

在Java中&#xff0c;线程分为用户线程和守护线程。用户线程是程序的主要线程&#xff0c;而守护线程则是为用户线程提供支持的后台线程。以下是对这两种线程的详细解释以及示例代码。 用户线程与守护线程的区别 用户线程&#xff1a; 用户线程是应用程序中执行的主要线程&…

HarmonyOS NEXT 应用运行异常记录与解决(持续整理版)

问题一 App Launch Failed to get the device apiVersion. 解决方案 进入到设备管理&#xff0c;点击对应开启的模拟器设备&#xff0c;先将模拟器关闭&#xff0c;然后点击查出掉用户数据&#xff0c;再重启。 重启之后&#xff0c;一般能解决。如果遇到还是显示拿不到apiVer…

《第三十四章 高级主题 - 动画效果》

在 Android 应用开发中&#xff0c;动画效果能够显著提升用户体验&#xff0c;使应用更加生动和吸引人。本章将深入探讨 Android 中的动画效果&#xff0c;包括属性动画、帧动画以及 ViewPager 动画。 一、属性动画 &#xff08;一&#xff09;属性动画简介 属性动画是 Android…

bash反弹shell分析

目录 介绍步骤 介绍 与目标主机建立连接的原理是利用漏洞执行ShellCode。 GetShell的实质是&#xff1a;执行ShellCode&#xff0c;将目标主机的Shell重定向到攻击机。拿到Shell利于后续的渗透。 所谓的反弹Shell是指GetShell的过程由目标主机主动发起&#xff08;反向连接&a…

Git如何安装和配置

一、Git 的安装 1、Git 的官网 Git (git-scm.com) 2、下载 Git for Windows 在官网下载好 Git 的安装文件后&#xff0c;接下来就可以进行安装了。 3、安装 Git 以管理员身份运行你下载的安装文件&#xff1a; 然后就可以进入安装向导了。 4、验证是否安装成功 当安装向…

测试分类简介

1.按照测试⽬标分类 1&#xff09;界面测试&#xff1a; 界⾯测试&#xff08;简称UI测试)&#xff0c;指按照界⾯的需求&#xff08;⼀般是UI设计稿&#xff09;和界⾯的设计规则&#xff0c;对我们软件界⾯所展⽰的全部内容进⾏测试和检查。 2&#xff09;功能测试&#xff…

内存管理(三)--Linux CMA内存使用

文章目录 一、预留一段内存常用方法1.1 设备树定义reserve memory 二、内存节点常见属性2.1 no-map和reusable使用上的区别 三、预留CMA的reserved memory方法3.1 共享cma&#xff08;采用设备树&#xff09;3.2 共享cma&#xff08;采用bootargs&#xff09;3.3 私有cma 四、使…

【数据结构】--初始集合框架

1. 什么是集合框架 集合框架:Java中已经实现好的集合类&#xff08;一些已经由Java实现好的数据结构&#xff09;。Java的数据结构和C语言的数据结构是没有什么区别的&#xff0c;里面的区别就只是实现的语言不一样。不可能出现Java的单链表和C语言实现的单链表是两个不同的东…

idea配置svn发现安装的svn中的bin目录下没有svn.exe文件

问题描述 使用idea配置svn时&#xff0c;发现安装的svn没有svn.exe文件 解决方法 1、双击svn安装包&#xff0c;找到【modify】 2、默认安装时&#xff0c;没有安装command line client tools&#xff0c;里面是有svn.exe 3、选择will be installed on local hard drive 4、…

idea中使用git提交代码报错husky > pre-commit

1、原因&#xff1a; 本地项目中的.git下的一个文件导致的提交失败&#xff0c;这个文件是pre-commit文件。由于预提交钩子pre-commit导致的提交失败&#xff0c;通过删除.git/hooks/pre-commit文件可以解决 2、解决步骤&#xff1a; 1、找到项目中的.git文件夹&#xff0c;…

鸿蒙-右边固定长度,左边超长Text自适应

Component Entry struct test {build() {Row() {Column() {Text(长字符串长字符串长字符串长字符串长字符串长字符串长字符串长字符串长字符串长字符串长字符串长字符串长字符串长字符串长字符串长字符串长字符串).maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis…

公园智能厕所引导大屏,清楚显示厕位有无人状态

在科技飞速发展的今天&#xff0c;公园的设施也在不断与时俱进。其中&#xff0c;公园智能厕所引导大屏的出现&#xff0c;为游客带来了全新的如厕体验。 走进公园的智能厕所区域&#xff0c;首先映入眼帘的便是那醒目的引导大屏。屏幕上清晰地显示着各个厕位的有无人状态&…

sqlite3的db.parallelize方法:并行执行SQL语句,提升数据库操作效率

在Node.js环境中&#xff0c;sqlite3作为一个广受欢迎的轻量级数据库库&#xff0c;为开发者提供了一个与SQLite数据库进行交互的简洁API。在进行数据库操作时&#xff0c;为了提高效率&#xff0c;sqlite3提供了db.parallelize方法&#xff0c;允许并行执行多个SQL语句&#x…

ROS Simulink 支持与限制

ROS Simulink 支持与限制 ROS 工具箱在 Simulink 中不支持以下 ROS 功能&#xff1a; ROS 服务 服务器ROS 动作 如果您的应用需要这些功能&#xff0c;建议使用 MATLAB ROS 功能。您可以使用 MATLAB 编写一个 ROS 节点&#xff0c;该节点可以将服务、动作和变换树发布到一个…

一、登录接口-注意实现

一、如何保护cookie或jwt不被劫持 使用https对请求、响应信息进行加密&#xff0c;防止被攻击者中途劫持请求信息 二、什么是跨域&#xff1f;合理的设置跨域能够解决哪些问题 跨域是浏览器做出的一种安全策略&#xff0c;它要求发起请求的客户端必须和服务端保持相同的协议…

4款电脑数据恢复软件分享,告别天价恢复!

大家应该都经历过数据丢失吧&#xff0c;这不&#xff0c;前两天&#xff0c;我那电脑不知怎的&#xff0c;突然就把我辛苦整理的文件夹给“咔嚓”一声删掉了。这下可好&#xff0c;我那里面可是有我好几个月的心血啊&#xff01;好在&#xff0c;我之前也发生过类似的事情&…

【通俗理解】最优控制之旅——强化学习中的策略优化

【通俗理解】最优控制之旅——强化学习中的策略优化 关键词提炼 #最优控制 #强化学习 #状态值函数 #奖励函数 #折扣因子 #贝尔曼方程 #策略迭代 #值迭代 #动态规划 #马尔可夫决策过程 第一节&#xff1a;最优控制的类比与核心概念 1.1 最优控制的类比 最优控制就像是一位精…

视联动力数字科技新成果闪耀2024数博会

在2024年的8月28日至30日&#xff0c;贵阳举办的中国国际大数据产业博览会上&#xff0c;视联动力带来了一系列引人注目的技术和创新应用。 这场为期三天的展览会展示了公司在数字经济发展领域的最新成就&#xff0c;特别是国产通信协议技术方面的进展。 视联动力重点展出了算…

QGraphicsview相关学习

文章学习自&#xff1a; Qt_绘图框架_QGraphicsview实现缩放移动图片_Livy0123的博客-CSDN博客 这里进行一些自己的分析和理解。 (1) 自定义类MyGraphicsView继承自QGraphicsView 核心是重定义的滚轮事件。 [virtual protected] void QGraphicsView::wheelEvent(QWheelEve…