存储数组的内存空间是连续的
存储链表的内存空间不是连续的
所有数据结构都是基于数组、链表或二者的组合实现的
链表在初始化后,仍可以在程序运行过程中对其长度进行调整,因此也称"动态数据结构"。数组在初始化后长度不可变,因此也称"静态数据结构"。值得注意的是,数组可通过重新分配内存实现长度变化,从而具备一定的"动态性"。
基本数据类型是 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:数组存储在栈上和存储在堆上,对时间效率和空间效率是否有影响?
存储在栈上和堆上的数组都被存储在连续内存空间内,数据操作效率基本一致。然而,栈和堆具有各自的特点,从而导致以下不同点。
- 分配和释放效率:栈是一块较小的内存,分配由编译器自动完成;而堆内存相对更大,可以在代码中动态分配,更容易碎片化。因此,堆上的分配和释放操作通常比栈上的慢。
- 大小限制:栈内存相对较小,堆的大小一般受限于可用内存。因此堆更加适合存储大型数组。
- 灵活性:栈上的数组的大小需要在编译时确定,而堆上的数组的大小可以在运行时动态确定。