面试题答案
一键面试内存管理方式的不同
- vector:
- 连续内存分配:
vector
在内存中分配一块连续的存储空间来存储元素。这意味着所有元素在内存中是紧密排列的,如同数组一样。例如,若vector
存储int
类型数据,第一个int
紧接着第二个int
存储,中间没有间隔。 - 动态扩展机制:当
vector
的容量(capacity)不足以容纳新元素时,它会重新分配内存。通常,新分配的内存大小是当前容量的两倍(不同实现可能略有差异)。然后将原有的元素复制到新的内存空间,最后释放旧的内存。
- 连续内存分配:
- list:
- 非连续内存分配:
list
采用链表结构,每个元素在内存中独立存储,通过指针相互连接。每个节点包含数据部分和指针部分,指针指向下一个(双向链表还包含指向前一个)节点。例如,第一个节点的数据和第二个节点的数据在内存中可能相隔甚远。 - 按需分配:当向
list
中插入新元素时,仅需为新元素分配一个新的节点空间,并调整相应的指针。删除元素时,只需释放该元素对应的节点内存,同时调整指针,不会影响其他元素的内存位置。
- 非连续内存分配:
对实际应用中性能表现的影响
- 访问性能:
- vector:由于元素在内存中连续存储,支持随机访问。可以通过索引(
[]
操作符)快速访问任何位置的元素,时间复杂度为O(1)。例如,访问vector[i]
可以直接通过内存地址计算快速定位到该元素。 - list:因为元素非连续存储,不支持随机访问。若要访问第
n
个元素,需要从链表头(或尾)开始,通过指针逐个遍历节点,时间复杂度为O(n)。例如,要访问链表中间某个位置的元素,必须从头开始依次移动指针。
- vector:由于元素在内存中连续存储,支持随机访问。可以通过索引(
- 插入和删除性能:
- vector:在
vector
的中间插入或删除元素时,由于要保持内存连续性,需要移动大量元素。例如,在vector
的开头插入元素,后续所有元素都要向后移动,时间复杂度为O(n)。在尾部插入元素,如果当前容量足够,时间复杂度为O(1);若容量不足导致重新分配内存,时间复杂度为O(n)。 - list:在
list
的任何位置插入或删除元素,仅需调整指针,不需要移动其他元素,时间复杂度为O(1)。例如,在链表中间插入一个新节点,只需修改前一个节点和后一个节点的指针即可。
- vector:在
- 内存使用效率:
- vector:由于是连续内存分配,在内存使用上相对紧凑,内存碎片较少。但如果频繁进行动态扩展,可能会导致一些额外的内存浪费(因为每次扩展可能会多分配一些空间)。
- list:每个节点除了存储数据外,还需要额外的指针空间,因此在存储相同数量元素时,
list
通常比vector
占用更多的内存。而且链表结构容易产生内存碎片,因为节点在内存中分散存储。