数据结构 数组与链表 内存与缓存

存储数组的内存空间是连续的

存储链表的内存空间不是连续的

所有数据结构都是基于数组、链表或二者的组合实现的

链表在初始化后,仍可以在程序运行过程中对其长度进行调整,因此也称"动态数据结构"。数组在初始化后长度不可变,因此也称"静态数据结构"。值得注意的是,数组可通过重新分配内存实现长度变化,从而具备一定的"动态性"。

基本数据类型是 CPU 可以直接进行运算的类型,在算法中直接被使用

基本数据类型以二进制的形式存储在计算机中。一个二进制位即为1 比特。

现代计算机 CPU 通常将 1 字节作为最小寻址内存单元。

基本数据类型提供了数据的"内容类型",而数据结构提供了数据的"组织方式"

数字是以"补码"的形式存储在计算机中的。

负数的原码不能直接用于运算

先将原码转换为反码,并在反码下计算 ,最后将结果从反码转换回原码,则可得到正确结果

计算机内部的硬件电路主要是基于加法运算设计的。这是因为加法运算相对于其他运算(比如乘法、除法和减法)来说,硬件实现起来更简单,更容易进行并行化处理,运算速度更快。

数组

存储在堆上(需要手动释放空间)

int *arr1 = new int5 {};

索引本质上是内存地址的偏移量。首个元素的地址偏移量是0 ,因此它的索引为0是合理的。

链表

  • 链表的首个节点被称为"头节点",最后一个节点被称为"尾节点"。尾节点指向的是"空"

在相同数据量下,链表比数组占用更多的内存空间。

在链表中访问节点的效率较低。如上一节所述,我们可以在O(1) 时间下访问数组中的任意元素。链表则不然,程序需要从头节点出发,逐个向后遍历,直至找到目标节点。也就是说,访问链表的第 个节点需要循环i-1 轮,时间复杂度为 O(n)

typedef struct listNode

{

int val;

struct listNode* next;

}listNode;

listNode* newmistNode(int val)

{

listNode* node;

node = (listNode*)malloc(sizeof(listNode));

node->val = val;

node->next = NULL;

return node;

}

//链表的初始化

listNode* n0 = newlistNode(1);

listNode* n1 = newlistNode(2);

listNode* n2 = newlistNode(3);

n0->next = n1;

n1->next = n2;

n2->next = NULL;

//插入节点

void inert(listNode* n0, listNode* p)

{

listNode* n1 = n0->next;

p->next = n1;

n0->next = p;

}

//删除节点

void removeItem(listNode* n0)

{

if (!n0->next)

{

return;

}

listNode* p = n0->next;

listNode* n1 = p->next;

n0->next = n1;

free(p);

}

//访问节点

listNode* access(listNode* head, int index)

{

for (int i = 0;i < index;i++)

{

if (head == NULL)

{

return NULL;

}

head = head->next;

}

return head;

}

//查找节点

int find(listNode* head, int target)

{

int index = 0;

while (head)

{

if (head->val == target)

{

return index;

}

head = head->next;

index++;

}

return -1;

}

列表

  • 链表天然可以看作一个列表,其支持元素增删查改操作,并且可以灵活动态扩容。
  • 数组也支持元素增删查改,但由于其长度不可变,因此只能看作一个具有长度限制的列表。

++动态数组(dynamic array)++来实现列表

计算机存储设备

计算机中包括三种类型的存储设备:++硬盘(hard disk)++ 、++内存(random-access memory, RAM)++ 、++缓存(cache memory)++。

| 硬盘 | 内存 | 缓存 |
|---------|----------------------|---------------------|-----------------------------|
| 用途 | 长期存储数据,包括操作系统、程序、文件等 | 临时存储当前运行的程序和正在处理的数据 | 存储经常访问的数据和指令,减少 CPU 访问内存的次数 |
| 易失性 | 断电后数据不会丢失 | 断电后数据会丢失 | 断电后数据会丢失 |
| 容量 | 较大,TB 级别 | 较小,GB 级别 | 非常小,MB 级别 |
| 速度 | 较慢,几百到几千 MB/s | 较快,几十 GB/s | 非常快,几十到几百 GB/s |
| 价格(人民币) | 较便宜,几毛到几元 / GB | 较贵,几十到几百元 / GB | 非常贵,随 CPU 打包计价 |

越靠近金字塔顶端的存储设备的速度越快、容量越小、成本越高。这种多层级的设计并非偶然,而是计算机科学家和工程师们经过深思熟虑的结果。

  • 硬盘难以被内存取代。首先,内存中的数据在断电后会丢失,因此它不适合长期存储数据;其次,内存的成本是硬盘的几十倍,这使得它难以在消费者市场普及。
  • 缓存的大容量和高速度难以兼得。随着 L1、L2、L3 缓存的容量逐步增大,其物理尺寸会变大,与 CPU 核心之间的物理距离会变远,从而导致数据传输时间增加,元素访问延迟变高。在当前技术下,多层级的缓存结构是容量、速度和成本之间的最佳平衡点。

硬盘用于长期存储大量数据,内存用于临时存储程序运行中正在处理的数据,而缓存则用于存储经常访问的数据和指令,

缓存可以看作 CPU 的一部分,它通过智能地从内存加载数据,给 CPU 提供高速的数据读取,从而显著提升程序的执行效率,减少对较慢的内存的依赖。

数组具有更高的缓存命中率,因此它在操作效率上通常优于链表。这使得在解决算法问题时,基于数组实现的数据结构往往更受欢迎

Q:数组存储在栈上和存储在堆上,对时间效率和空间效率是否有影响?

存储在栈上和堆上的数组都被存储在连续内存空间内,数据操作效率基本一致。然而,栈和堆具有各自的特点,从而导致以下不同点。

  1. 分配和释放效率:栈是一块较小的内存,分配由编译器自动完成;而堆内存相对更大,可以在代码中动态分配,更容易碎片化。因此,堆上的分配和释放操作通常比栈上的慢。
  2. 大小限制:栈内存相对较小,堆的大小一般受限于可用内存。因此堆更加适合存储大型数组。
  3. 灵活性:栈上的数组的大小需要在编译时确定,而堆上的数组的大小可以在运行时动态确定。
相关推荐
YSL0701241 小时前
一文通关-----树和二叉树
数据结构·算法
若亦_1 小时前
排序>>交换排序
java·数据结构·算法
努力努力再努力wz1 小时前
【Lua 入门系列】Lua数据结构:从 Table 出发,一文串联数组、链表、栈、队列、集合、映射与树的实现原理
数据结构·链表·lua
漂流瓶jz11 小时前
UVA-1612 猜名次 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·题解·排序·aoapc·算法竞赛入门经典
A boy CDEF girl17 小时前
算法题专栏:优选算法
数据结构·算法·leetcode
图灵-M20 小时前
【数据结构】静态 & 动态顺序表
c语言·数据结构·笔记
careathers21 小时前
【数据结构】基于比较的排序算法 - 选择排序
数据结构·算法·排序算法
XiYang-DING1 天前
P4104.环形链表判断
数据结构·链表
逆向编程1 天前
MATLAB高级数据结构:cell、struct与table深度应用
开发语言·数据结构·matlab