deque…
deque 是 double-ended queue 的缩写,又称双端队列容器。
前面章节中,我们已经系统学习了 vector 容器,值得一提的是,deque 容器和 vecotr 容器有很多相似之处,比如:
deque 容器也擅长在序列尾部添加或删除元素(时间复杂度为O(1)),而不擅长在序列中间添加或删除元素。
deque 容器也可以根据需要修改自身的容量和大小。
和 vector 不同的是,deque 还擅长在序列头部添加或删除元素,所耗费的时间复杂度也为常数阶O(1)。并且更重要的一点是,deque 容器中存储元素并不能保证所有元素都存储到连续的内存空间中。
#include#include using namespace std; int main() { deque d{1,2,3,4,5}; //从容器首元素,遍历至最后一个元素 for (auto i = d.begin(); i < d.end(); i++) { cout << *i << " "; } for (auto i = begin(d); i < end(d); i++) { cout << *i << " "; } return 0; }
当向 deque 容器添加元素时,deque 容器会申请更多的内存空间,同时其包含的所有元素可能会被复制或移动到新的内存地址(原来占用的内存会释放),这会导致之前创建的迭代器失效。
#include#include using namespace std; int main() { deque d{ 1,2,3,4 }; cout << d.at(1) << endl; d.at(1) = 5; cout << d.at(1) << endl; //下面这条语句会抛出 out_of_range 异常 //cout << d.at(10) << endl; return 0; }
为什么 deque 容器在重载 [] 运算符时,没有实现边界检查的功能呢?答案很简单,因为性能。如果每次访问元素,都去检查索引值,无疑会产生很多开销。当不存在越界访问的可能时,就能避免这种开销。
#includelist#include using namespace std; int main() { deque d; //调用push_back()向容器尾部添加数据。 d.push_back(2); //{2} //调用pop_back()移除容器尾部的一个数据。 d.pop_back(); //{} //调用push_front()向容器头部添加数据。 d.push_front(2);//{2} //调用pop_front()移除容器头部的一个数据。 d.pop_front();//{} //调用 emplace 系列函数,向容器中直接生成数据。 d.emplace_back(2); //{2} d.emplace_front(3); //{3,2} //emplace() 需要 2 个参数,第一个为指定插入位置的迭代器,第二个是插入的值。 d.emplace(d.begin() + 1, 4);//{3,4,2} for (auto i : d) { cout << i << " "; } //erase()可以接受一个迭代器表示要删除元素所在位置 //也可以接受 2 个迭代器,表示要删除元素所在的区域。 d.erase(d.begin());//{4,2} d.erase(d.begin(), d.end());//{},等同于 d.clear() return 0; }
STL list 容器,又称双向链表容器,即该容器的底层是以双向链表的形式实现的。这意味着,list 容器中的元素可以分散存储在内存空间里,而不是必须存储在一整块连续的内存空间中。
#include#include #include using namespace std; int main() { std::list
values{ 1,2 }; //第一种格式用法 values.insert(values.begin() , 3);//{3,1,2} //第二种格式用法 values.insert(values.end(), 2, 5);//{3,1,2,5,5} //第三种格式用法 std::array test{ 7,8,9 }; values.insert(values.end(), test.begin(), test.end());//{3,1,2,5,5,7,8,9} //第四种格式用法 values.insert(values.end(), { 10,11 });//{3,1,2,5,5,7,8,9,10,11} for (auto p = values.begin(); p != values.end(); ++p) { cout << *p << " "; } return 0; }
#include#include using namespace std; int main() { //创建并初始化 2 个 list 容器 list
mylist1{ 1,2,3,4 }, mylist2{10,20,30}; list ::iterator it = ++mylist1.begin(); //指向 mylist1 容器中的元素 2 //调用第一种语法格式 mylist1.splice(it, mylist2); // mylist1: 1 10 20 30 2 3 4 // mylist2: // it 迭代器仍然指向元素 2,只不过容器变为了 mylist1 //调用第二种语法格式,将 it 指向的元素 2 移动到 mylist2.begin() 位置处 mylist2.splice(mylist2.begin(), mylist1, it); // mylist1: 1 10 20 30 3 4 // mylist2: 2 // it 仍然指向元素 2 //调用第三种语法格式,将 [mylist1.begin(),mylist1.end())范围内的元素移动到 mylist.begin() 位置处 mylist2.splice(mylist2.begin(), mylist1, mylist1.begin(), mylist1.end());//mylist1: //mylist2:1 10 20 30 3 4 2 cout << "mylist1 包含 " << mylist1.size() << "个元素" << endl; cout << "mylist2 包含 " << mylist2.size() << "个元素" << endl; //输出 mylist2 容器中存储的数据 cout << "mylist2:"; for (auto iter = mylist2.begin(); iter != mylist2.end(); ++iter) { cout << *iter << " "; } return 0; }
forward_list 是 C++ 11 新添加的一类容器,其底层实现和 list 容器一样,采用的也是链表结构,只不过 forward_list 使用的是单链表,而 list 使用的是双向链表(如图 1 所示)。



