简介: CSDN博客专家、《Android系统多媒体进阶实战》一书作者
新书发布:《Android系统多媒体进阶实战》🚀
优质专栏: Audio工程师进阶系列【原创干货持续更新中……】🚀
优质专栏: 多媒体系统工程师系列【原创干货持续更新中……】🚀
优质视频课程:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
🍉🍉🍉文章目录🍉🍉🍉
🌻1.前言
本篇目的:C++进阶之std::deque插入与删除效率
🌻2. C++进阶之std::deque插入与删除效率介绍
-
基本概念
std::deque
是 C++ 标准库中的双端队列,支持在队列的两端进行插入和删除操作。与std::vector
不同,std::deque
的元素存储在多个不连续的内存块中,这使得它在处理双端插入和删除时具有独特的效率优势。 -
尾部插入与删除
在std::deque
中,尾部的插入和删除操作非常高效,时间复杂度为O(1)
。这与std::vector
类似,因为两者都能在不需要移动其他元素的情况下,直接在尾部添加或删除元素。 -
头部插入与删除
std::deque
的一个优势是支持在头部进行插入和删除,且其时间复杂度为O(1)
。由于std::deque
使用多个内存块来存储元素,在插入或删除时,不需要移动整个元素序列,只需操作相关内存块的指针。因此,头部插入和删除的性能较为高效。 -
内存块管理
std::deque
内部采用分段存储,元素被分配到多个内存块中。当需要扩展时,它只需要分配新的内存块,而不是移动整个元素序列。这使得在两端进行插入和删除时不需要频繁地进行内存重分配和元素搬移,因此具有较高的插入删除效率。 -
中间位置插入与删除
尽管std::deque
在两端的插入和删除效率较高,但在中间位置插入和删除的效率较低,时间复杂度为O(n)
。这主要是因为中间位置的插入或删除需要调整多个内存块中的元素,可能会导致较高的开销。因此,std::deque
不适合频繁在中间位置进行操作。 -
整体性能
std::deque
提供了高效的头部和尾部插入删除,但它的整体性能也会受到内存块管理的影响。在极端情况下,如果中间频繁进行插入和删除操作,性能可能会受到影响。因此,选择std::deque
作为容器时,应根据应用场景来评估其性能优势。 -
扩展与缩减
在扩展时,std::deque
不会像std::vector
一样需要重新分配并复制整个数组,只需追加新的内存块。相反,当元素删除时,std::deque
会回收内存块,避免了不必要的内存浪费,保持了较好的内存管理。 -
适用场景
std::deque
非常适合用于需要频繁在两端插入和删除元素的场景。例如,在实现双端队列、任务调度或缓存等应用时,std::deque
的高效插入和删除操作使其成为理想选择。
🌻3. 代码实例
🐓3.1 末尾插入与删除操作
-
1.应用场景:
std::deque 适用于 末尾插入与删除,并且其在末尾的插入和删除操作具有 常数时间复杂度。
这种操作常见于队列或缓冲区的实现。
-
2.用法实例
#include <iostream>
#include <deque>
#include <chrono>
int main() {
const int N = 100000;
std::deque<int> dq;
// 末尾插入
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; ++i) dq.push_back(i);
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Deque 末尾插入时间: "
<< std::chrono::duration<double>(end - start).count() << "s\n";
// 末尾删除
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; ++i) dq.pop_back();
end = std::chrono::high_resolution_clock::now();
std::cout << "Deque 末尾删除时间: "
<< std::chrono::duration<double>(end - start).count() << "s\n";
return 0;
}
push_back() 和 pop_back() 在 std::deque 中是 常数时间操作,不受容器大小的影响。
🐓3.2 头部插入与删除操作
-
1.应用场景:
std::deque 适用于 头部插入与删除,但 其效率要优于 std::vector,在头部插入和删除时的时间复杂度为 O(1),而不是 std::vector 中的 O(N) 操作。
这种操作常见于双端队列的应用,类似于环形缓冲区。
-
2.用法实例
#include <iostream>
#include <deque>
#include <chrono>
int main() {
const int N = 100000;
std::deque<int> dq;
// 头部插入
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; ++i) dq.push_front(i);
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Deque 头部插入时间: "
<< std::chrono::duration<double>(end - start).count() << "s\n";
// 头部删除
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; ++i) dq.pop_front();
end = std::chrono::high_resolution_clock::now();
std::cout << "Deque 头部删除时间: "
<< std::chrono::duration<double>(end - start).count() << "s\n";
return 0;
}
push_front() 和 pop_front() 在 std::deque 中是 常数时间操作,比 std::vector 在头部插入和删除的 O(N) 更高效。
🐓3.3 随机访问与插入中间元素的效率
-
1.应用场景:
std::deque 提供了 随机访问,并且可以在 两端插入或删除 元素时保持 O(1) 的复杂度,但在 中间插入或删除 时,仍然需要 O(N) 的时间复杂度。
这种操作对于需要快速两端操作而中间操作不频繁的场景非常适用。
-
2.用法实例
#include <iostream>
#include <deque>
#include <chrono>
int main() {
const int N = 100000;
std::deque<int> dq(N, 0); // 初始包含 N 个元素
// 随机访问
auto start = std::chrono::high_resolution_clock::now();
volatile int x = dq[N / 2]; // 访问中间元素
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Deque 随机访问时间: "
<< std::chrono::duration<double>(end - start).count() << "s\n";
// 中间插入
start = std::chrono::high_resolution_clock::now();
dq.insert(dq.begin() + N / 2, 42); // 在中间插入元素
end = std::chrono::high_resolution_clock::now();
std::cout << "Deque 中间插入时间: "
<< std::chrono::duration<double>(end - start).count() << "s\n";
return 0;
}
operator[] O(1),可以直接进行随机访问。
中间插入和删除需要 移动元素,时间复杂度为 O(N)。