数据结构与算法(2):顺序表与链表

1.前言

哈喽大家好喔,今天博主继续进行数据结构的分享与学习,今天的主要内容是顺序表与链表,是最简单但又相当重要的数据结构,为以后的学习有重要的铺垫,希望大家一起交流学习,互相进步,让我们开始吧。

2.正文

数据结构是计算机科学中非常重要的一部分,用于组织和管理数据以便高效地访问和修改。顺序表和链表是两种常见且基础的数据结构,它们各有特点和适用场景。以下是对这两种数据结构的详细分析:

2.1顺序表

顺序表是在计算机内存中以数组的形式保存的线性表。其有点类似数组,但添加了几个额外的概念。线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素,使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中。

这里面有俩个定义顺序表功能的俩个变量:分别是count和size变量,size是用来确定顺序表的存储空间大小的,count是用来标记顺序表中当前元素位置的。

2.1.1结构特点:

  1. 物理存储连续性:顺序表中的所有元素在内存中占有一段连续的存储空间。
  2. 随机访问:顺序表支持通过下标快速访问任意位置的元素,访问时间复杂度为O(1)。
  3. 空间分配
    • 静态顺序表:使用定长数组存储元素,适用于数据大小已知的场景,但灵活性较差。
    • 动态顺序表:使用动态开辟的数组存储,可以根据需要动态调整存储空间大小,更加灵活。
  4. 插入和删除操作:由于物理连续性,插入和删除操作需要移动元素,效率较低,时间复杂度为O(n)。

2.1.2结构定义:

typedef struct vector {int size, count;int *data;//总大小,元素数量
//指针指向连续的存储区
} vector;
//初始化
vector *getNewVector(int n) {vector *p = (vector *)malloc(sizeof(vector));p->size = n;//存储上限p->count = 0;p->data = (int *)malloc(sizeof(int) * n);return p;
}

2.1.3结构操作:


2.1.3.1插入操作:
//插入操作
int insert(vector *v, int pos, int val) {if (pos < 0 || pos > v->count) return 0;if (v->size == v->count && !expand(v)) return 0;for (int i = v->count - 1; i >= pos; i--) {v->data[i + 1] = v->data[i];}v->data[pos] = val;v->count += 1;return 1;
}

 2.1.3.2删除操作:
//删除操作
void clear(vector *v) {if (v == NULL) return ;free(v->data);free(v);return ;
}

 2.1.3.3销毁操作:
//销毁操作
int erase(vector *v, int pos) {if (pos < 0 || pos >= v->count) return 0;for (int i = pos + 1; i < v->count; i++) {v->data[i - 1] = v->data[i];}v->count -= 1;return 1;
}

2.1.3.4自动扩容操作 :

需要顺序表扩容的俩个条件:

  • 插入到非法位置,即位置小于0或大于等于size。
  • 顺序表满了之后如果还需要添加新的元素,就需要扩容。

realloc是 C 标准库中的一个函数,用于调整已经分配的内存块的大小。这个函数常用于动态内存管理,特别是在需要扩展或缩小已经分配的内存时。

realloc开辟内存的三种情况:

  1. 如果当前内存段后面有足够的空间来满足新的大小要求,realloc 会尝试扩展这段内存空间,并返回原指针。
  2. 如果当前内存段后面的空闲字节不够,realloc 会在堆中查找一个能够满足新大小要求的内存块,将原数据复制到新的位置,并释放原来的内存块,然后返回新内存块的首地址。
  3. 如果重新分配失败(例如,因为内存不足),realloc 会返回 NULL,但此时原始内存块仍然保持有效。

注意事项:

  1. 返回值处理:使用 realloc 时,应始终检查其返回值。如果返回 NULL,表示重新分配失败,此时原始内存块仍然有效,需要适当处理(如释放原始内存块)。
  2. 指针更新:如果 realloc 成功并返回一个新的地址(不同于原始地址),则需要更新指向该内存块的指针,以避免内存泄漏或野指针问题。
  3. 内存释放:当内存不再使用时,应使用 free() 函数释放内存块。如果 realloc 失败且原始内存块仍需保留,则不应调用 free() 释放它。
  4. 数据保留realloc 会保留原始内存块中的数据,并将其复制到新的内存块(如果分配了新的内存块)。但是,如果新大小小于原大小,则超出的数据可能会丢失。
//扩容操作
int expand(vector *v) {if (v == NULL) return 0;printf("expand v from %d to %d\n", v->size, 2 * v->size);int *p = (int *)realloc(v->data, sizeof(int) * 2 * v->size);if (p == NULL) return 0;v->data = p;v->size *= 2;return 1;
}

 完整调试代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>typedef struct vector {int size, count;int *data;//总大小,元素数量
//指针指向连续的存储区
} vector;
//初始化
vector *getNewVector(int n) {vector *p = (vector *)malloc(sizeof(vector));p->size = n;//存储上限p->count = 0;p->data = (int *)malloc(sizeof(int) * n);return p;
}int expand(vector *v) {if (v == NULL) return 0;printf("expand v from %d to %d\n", v->size, 2 * v->size);int *p = (int *)realloc(v->data, sizeof(int) * 2 * v->size);if (p == NULL) return 0;v->data = p;v->size *= 2;return 1;
}
//插入操作
int insert(vector *v, int pos, int val) {if (pos < 0 || pos > v->count) return 0;if (v->size == v->count && !expand(v)) return 0;for (int i = v->count - 1; i >= pos; i--) {v->data[i + 1] = v->data[i];}v->data[pos] = val;v->count += 1;return 1;
}
//销毁操作
int erase(vector *v, int pos) {if (pos < 0 || pos >= v->count) return 0;for (int i = pos + 1; i < v->count; i++) {v->data[i - 1] = v->data[i];}v->count -= 1;return 1;
}void output_vector(vector *v) {int len = 0;for (int i = 0; i < v->size; i++) {len += printf("%3d", i);}printf("\n");for (int i = 0; i < len; i++) printf("-");printf("\n");for (int i = 0; i < v->count; i++) {printf("%3d", v->data[i]);}printf("\n");printf("\n\n");return ;
}
//删除操作
void clear(vector *v) {if (v == NULL) return ;free(v->data);free(v);return ;
}int main() {srand(time(0));#define MAX_OP 20vector *v = getNewVector(2);for (int i = 0; i < MAX_OP; i++) {int op = rand() % 4, pos, val, ret;switch (op) {case 0:case 1:case 2:pos = rand() % (v->count + 2);//为了可以看到插入到非法位置时程序的反应val = rand() % 100;ret = insert(v, pos, val);printf("insert %d at %d to vector = %d\n", val, pos, ret);break;case 3:pos = rand() % (v->count + 2);ret = erase(v, pos);printf("erase item at %d in vector = %d\n", pos, ret);break;}output_vector(v);}clear(v);return 0;
}

2.2链表

链表是一种物理存储结构上非 连续、非顺序的存储结构,但逻辑上是顺序的。链表由一系列结点(链表中每一个元素称为结点)组成,每个结点包含两个部分:数据域和指针域。数据域用来存储数据,而指针域则用来指向下一个结点。

2.2.1链表的分类:

  1. 单链表:每个节点只包含一个指针,指向下一个节点。
  2. 双链表:每个节点包含两个指针,一个指向前一个节点,另一个指向下一个节点。
  3. 循环链表:链表的最后一个节点的指针域指向第一个节点,形成一个环。

2.2.2结构特点:

  1. 物理存储非连续性:链表中的元素可以存储在内存中的任意位置,通过指针链接。
  2. 插入和删除操作高效:链表的插入和删除操作只需要修改指针指向,不需要移动大量元素,时间复杂度为O(1)。
  3. 不支持随机访问:链表的访问需要从头节点开始依次遍历到目标节点,访问效率较低。
  4. 空间利用:链表中的每个节点都需要额外的空间来存储指针,但链表可以动态增长,不需要预分配固定大小的空间。

2.2.3结构定义:

typedef struct Lnode
{int data;struct Lnode *next;
} Lnode, *Linklist;void InitList (Linklist *L)   //二级指针的目的是地址传递,因为该函数没有返回值,用地址传递带回头节点地址。
{Linklist p;p = (Linklist)malloc(sizeof(Lnode));if(p == NULL)cout << "申请内存空间失败。" << endl;p->next=NULL;*L = p;flag++;
}//初始化一个空的链表

2.2.4结构操作:

接下来主要介绍几个链表的基础关键操作:


2.2.4.1创建链表操作:
void Creatlist(Linklist L,int n)
{int i;	Lnode *p,*pt;pt=L;for(i=1;i<=n;i++){	p=(Linklist)malloc(sizeof(Lnode));if(p==NULL)cout << "申请内存空间失败。" << endl;cout << "请输入链表中元素:" << endl;cin >> p->data;		p->next=pt->next;pt->next=p;pt=p;}//flag++;
}//创建链表

 2.2.4.2查找操作:
int Getelem(Linklist L, int i)
{Lnode *p;int e;int j;p=L->next;j=1;while (p && j<i){p=p->next;++j;}if(!p || j>i)cout << "该位序不存在。" << endl;else{e=p->data;cout << "第" << i << "个元素为:" << e << endl;}return OK;
}//用e返回L中第i个数据元素的值.

 2.2.4.3插入操作:
int Listinsert(Linklist L,int i,int e)
{int j;Lnode *p,*s;p=L; j=0;while(p && j<i-1){p=p->next;++j;}if(!p || j>i-1)return ERROR;s=(Linklist)malloc(sizeof(Lnode));if(s==NULL)cout << "申请内存空间失败。" << endl;s->data=e;s->next=p->next;p->next=s;cout << "插入的元素是:" << e << endl;return OK;
}//在第i个位置插入元素e

2.2.4.1销毁链表操作:
int DestroyList(Linklist L)
{Lnode *p;p=NULL;if(L && flag!=0){while(L){   p=L;L=L->next;free(p);	}cout << "链表已销毁。" << endl;}elsecout << "链表不存在。" << endl;return OK;flag++;
}//销毁链表

2.3顺序表和链表的比较:

这边列举一个表格方便大家对着俩个功能类似的数据结构的特点有更加充分的理解:

特点顺序表链表
存储方式连续存储非连续存储
随机访问支持(O(1))不支持(需要遍历)
插入/删除操作效率低(O(n))效率高(O(1))
空间利用较高(无额外指针空间)较低(每个节点需额外空间存储指针)
灵活性较低(需要预分配空间)较高(可以动态增长)

3.小结

今天数据结构第二讲:顺序表与链表到这里就结束了,未来俩天会有链表相关延展问题实现的博客出炉,不要忘了点一手关注再走哦,希望喜欢的朋友多多支持我哦~

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

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

相关文章

数据结构之跳表SkipList、ConcurrentSkipListMap

概述 SkipList&#xff0c;跳表&#xff0c;跳跃表&#xff0c;在LevelDB和Lucene中都广为使用。跳表被广泛地运用到各种缓存实现当中&#xff0c;跳跃表使用概率均衡技术而不是使用强制性均衡&#xff0c;因此对于插入和删除结点比传统上的平衡树算法更为简洁高效。 Skip lis…

AQS详解(详细图文)

目录 AQS详解1、AQS简介AbstractQueuedSynchronizer的继承结构和类属性AQS的静态内部类Node总结AQS的实现思想总结AQS的实现原理AQS和锁的关系 2、AQS的核心方法AQS管理共享资源的方式独占方式下&#xff0c;AQS获取资源的流程详解独占方式下&#xff0c;AQS释放资源的流程详解…

如何通过DBC文件看懂CAN通信矩阵

实现汽车CAN通信开发&#xff0c;必不可少要用到DBC文件和CAN通信矩阵。 CAN通信矩阵是指用于描述 CAN 网络中各个节点之间通信关系的表格或矩阵。它通常记录了每个节点能够发送和接收的消息标识符&#xff08;ID&#xff09;以及与其他节点之间的通信权限。 通信矩阵在 CAN 网…

利用Msfvenom获取WindowsShell

一、在kali主机上利用msfvenom生成windows端的安装程序(exe文件),程序名最好取一个大家经常安装的程序,如腾讯视频、爱奇艺等。 (1)由于生成的程序可能会被杀毒软件识别,我们比较一下使用单个编码器生成的程序与用两个编码器生成的程序,哪个更容易被识别。 利用单个编码…

SSE(Server Sent Event)实战(2)- Spring MVC 实现

一、服务端实现 使用 RestController 注解创建一个控制器类&#xff08;Controller&#xff09; 创建一个方法来创建一个客户端连接&#xff0c;它返回一个 SseEmitter&#xff0c;处理 GET 请求并产生&#xff08;produces&#xff09;文本/事件流 (text/event-stream) 创建…

如何使用Milvus Cloud进行稀疏向量搜索

如何使用Milvus Cloud进行向量搜索Milvus Cloud 是一款高度可扩展、性能出色的开源向量数据库。在最新的 2.4 版本中,Milvus Cloud 支持了稀疏和稠密向量(公测中)。本文将利用 Milvus Cloud 2.4 来存储数据集并执行向量搜索。 接下来,我们将演示如何利用 Milvus Cloud 在 M…

[GXYCTF2019]Ping Ping Ping1

打开靶机 结合题目名称&#xff0c;考虑是命令注入&#xff0c;试试ls 结果应该就在flag.php。尝试构造命令注入载荷。 cat flag.php 可以看到过滤了空格,用 $IFS$1替换空格 还过滤了flag&#xff0c;我们用字符拼接的方式看能否绕过,ag;cat$IFS$1fla$a.php。注意这里用分号间隔…

睡前故事—绿色科技的未来:可持续发展的梦幻故事

欢迎来到《Bedtime Stories Time》。这是一个我们倾听、放松、并逐渐入睡的播客。感谢你收听并支持我们&#xff0c;希望你能将这个播客作为你睡前例行活动的一部分。今晚我们将讲述绿色科技的未来&#xff1a;可持续发展的梦幻故事的故事。一个宁静的夜晚&#xff0c;希望你现…

0602STM32定时器输出比较

STM32定时器输出比较 PWM简介 主要用来输出PWM波形&#xff0c;PWM波形又是驱动电机的必要条件&#xff0c;所以如果想用STM32做一些有电机的项目&#xff0c;比如智能车&#xff0c;机器人等。那输出比较功能就要认真掌握 1.PWM驱动LED呼吸灯 2.PWM驱动舵机 3.PWM驱动直流电机…

搜维尔科技:【研究】触觉技术将在5年内以8种方式改变人们的世界

触觉技术在过去几年中发展迅猛&#xff0c;大大提高了反馈的精确度和真实度。其应用产生了真正的影响&#xff0c;数百家公司和企业都集成了触觉技术来增强培训和研究模拟。 虽然触觉技术主要用于 B2B 层面&#xff0c;但触觉技术可能会彻底改变我们的生活&#xff0c;尤其是通…

视频共享融合赋能平台LntonCVS视频监控业务平台技术方案详细介绍

LntonCVS国标视频综合管理平台是一款智慧物联应用平台&#xff0c;核心技术基于视频流媒体&#xff0c;采用分布式和负载均衡技术开发&#xff0c;提供广泛兼容、安全可靠、开放共享的视频综合服务。该平台功能丰富&#xff0c;包括视频直播、录像、回放、检索、云存储、告警上…

【数据结构】详解堆

一、堆的概念 堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵 完全二叉树的 数组对象。 堆是非线性数据结构&#xff0c;相当于一维数组&#xff0c;有两个直接后继。 如果有一个关键码的集合K { k₀&#xff0c;k₁&#xff0c;k₂ &#xff0…

数据结构(双向链表)

链表的分类 链表的结构⾮常多样&#xff0c;以下情况组合起来就有8种&#xff08;2 x 2 x 2&#xff09;链表结构&#xff1a; 虽然有这么多的链表的结构&#xff0c;但是我们实际中最常⽤还是两种结构&#xff1a;单链表和双向带头循环链表 1.⽆头单向⾮循环链表&#xff1a…

第十课:telnet(远程登入)

如何远程管理网络设备&#xff1f; 只要保证PC和路由器的ip是互通的&#xff0c;那么PC就可以远程管理路由器&#xff08;用telnet技术管理&#xff09;。 我们搭建一个下面这样的简单的拓扑图进行介绍 首先我们点击云&#xff0c;把云打开&#xff0c;点击增加 我们绑定vmn…

【面试题】Redo log和Undo log

Redo log 介绍Redo log之前我们需要了解一下&#xff0c;mysql数据操作的流程&#xff1a; 上述就是数据操作的流程图&#xff0c;可以发现sql语句并不是直接操作的磁盘而是通过操作内存&#xff0c;然后进行内存到磁盘的一个同步。这里我们必须要了解一些区域&#xff1a; 缓…

华为HCIP Datacom H12-821 卷42

42.填空题 如图所示&#xff0c;MSTP网络中SW1为总根&#xff0c;请将以下交换机与IST域根和主桥配对。 参考答案&#xff1a;主桥1468 既是IST域根又是主桥468 既不是又不是就是25 解析&#xff1a; 主桥1468 既是IST域根又是主桥468 既不是又不是就是25 43.填空题 网络有…

[日进斗金系列]用码上飞解决企微开发维修管理系统的需求

前言&#xff1a; 今天跟大家唠唠如何用小money生 大money的方法&#xff0c;首先我们需要准备一个工具。 这个工具叫码上飞CodeFlying&#xff0c;它是目前国内首发的L4级自动化智能软件开发平台。 它可以在短时间内&#xff0c;与AI进行几轮对话就能开发出一个可以解决实际…

WEB前端06-BOM对象

BOM浏览器对象模型 浏览器对象模型&#xff1a;将浏览器的各个组成部分封装成对象。是用于描述浏览器中对象与对象之间层次关系的模型&#xff0c;提供了独立于页面内容、并能够与浏览器窗口进行交互的对象结构。 组成部分 Window&#xff1a;浏览器窗口对象 Navigator&…

区块链资料

Quantstamp - Public Security Assessments smart-contract-sanctuary-bsc/contracts/mainnet at master tintinweb/smart-contract-sanctuary-bsc GitHub https://github.com/slowmist/Cryptocurrency-Security-Audit-Guide/blob/main/README_CN.md sFuzz: 高效自适应的智…

Spring3(代理模式 Spring1案例补充 Aop 面试题)

目录 一、代理模式 介绍 意图 主要解决的问题 使用场景 实现方式 关键代码 应用实例 优点 缺点 使用建议 注意事项 结构 什么是代理模式&#xff1f; 为什么要用代理模式&#xff1f; 有哪几种代理模式&#xff1f; 1. 静态代理 实现 2. 基于接口的动态代理…