STL容器:deque

参考一

1. 概念
vector支持
随机访问每个元素,所需要的时间为常量,其在末尾增加或删除元素所需时间与元素数目无关,但是,在中间或开头增加或删除元素所需时间随元素数目呈线性变化。和vector相比,deque 是一个双向开口的连续线性空间,它允许在常数时间内对头端进行元素的插入或删除操作。需要注意的是,虽然deque也支持常数时间的随机访问,但是,由于其数据内存特点,其效率较vector会低很多,所以,除非有头部增删数据需求,否则建议使用vector。

2. API
deque提供了很多接口,以下列出deque的成员函数和操作:


(constructor)    Construct deque container (public member function)
(destructor)    Deque destructor (public member function)
operator=    Copy container content (public member function)

Iterators:
begin    Return iterator to beginning (public member function)
end    Return iterator to end (public member function)
rbegin    Return reverse iterator to reverse beginning (public member function)
rend    Return reverse iterator to reverse end (public member function)

Capacity:
size    Return size (public member function)
max_size    Return maximum size (public member function)
resize    Change size (public member functions)
empty    Test whether container is empty (public member function)

Element access:
operator[]    Access element (public member function)
at    Access element (public member function)
front    Access first element (public member function)
back    Access last element (public member function)

Modifiers:
assign    Assign container content (public member function)
push_back    Add element at the end (public member function)
push_front    Insert element at beginning (public member function)
pop_back    Delete last element (public member function)
pop_front    Delete first element (public member function)
insert    Insert elements (public member function)
erase    Erase elements (public member function)
swap    Swap content (public member function)
clear    Clear content (public member function)

Allocator:
get_allocator    Get allocator (public member function)

3. deque内存
deque从逻辑上来看是连续的内存,本质上是由一段段固定大小 的连续空间组成。deque采用类似索引的结构管理内存,如下:

image

采用一小块连续的内存索引缓存结点,每一个缓存结点也是一段连续的空间,可以存储多个数据。当索引内存空间满载,需要申请一块更大的内存做索引。

4. 为什么deque没有capacity和reserve

vector有capacity和reserve函数,deque和list一样,没有capacity和reserve函数。之所以这样,主要是因为list和deque两者都没有必要保留这两个函数,list是以1为增量动态增加内存,deque则是分段动态增加内存 。capacity返回当前所分配的内存块大小,vector在调用函数reserve(n)之后,其capacity将至少为N=n,reserve函数的作用是当现有内存空间小于N时重新分配内存。如果事先知道要插入N个元素,则与不断分配内存相比,预先调用reserve可以加快程序的运行速度

5. deque使用示例

代码如下:

#include
#include
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
    deque mydeque (3,1);     // 初始化deque为3个int,每个int值为1
    mydeque.push_front(2); //插入头
    mydeque.push_back(3); //插入尾
    cout << "mydeque size: " << (int) mydeque.size() << endl;
    cout << "mydeque contains:";
    for (unsigned i=0; i<mydeque.size();i++)    cout << " " << mydeque[i];
 
    system("pause");
    cout << endl;
    return 0;
}


 

输出结果:
mydeque size: 5
mydeque contains: 2 1 1 1 3

6. 迭代器失效

引用:
The C++ Standard Library:
Any insertion or deletion of elements other than at the beginning or end invalidates all pointers, references, and iterators that refer to elements of the deque.

SGI:
The semantics of iterator invalidation for deque is as follows. Insert (including push_front and push_back ) invalidates all iterators that refer to a deque. Erase in the middle of a deque invalidates all iterators that refer to the deque. Erase at the beginning or end of a deque (including pop_front and pop_back ) invalidates an iterator only if it points to the erased element.

Standard working draft:
An insert in the middle of the deque invalidates all the iterators and references to elements of the deque. An insert at either end of the deque invalidates all the iterators to the deque, but has no effect on the validity of references to elements of the deque."

总结如下:
1. 在deque中间 插入或者删除将使所有deque元素的迭代器、引用、指针失效
2. 在deque首部或者尾部插入元素会使迭代器失效,但不会引起引用和指针失效
3. 在其首部或尾部删除元素则只会使指向被删除元素的迭代器失效

deque迭代器失效较复杂,使用的时候需要小心。

7. 结语
deque支持随机访问每个元素,其所需要的时间为常量。在开头和末尾增加元素所需时间与元素数目无关,在中间增加或删除元素所需时间随元素数目呈线性变化。可动态增加或减少元素,内存管理自动完成,不提供用于内存管理的成员函数

 

参考二

私房STL之deque

一句话deque:deque是双端队列,它的空间构造并非一个vector式的长数组,而是“分段存储,整体维护”的方式;STL允许在deque中任意位置操作元素(删除添加)(这超出了deque的概念,最原始的deque将元素操作限定在队列两端),允许遍历,允许随机访问(这是假象);我们将看到,deque将是STL中stack和queue的幕后功臣,对deque做适当的修正,便可以实现stack和queue。

bug,deque_in_real

deque的迭代器

deque的迭代器与一般的迭代器不同,并不是vector或者list的普通指针式迭代器,有必要写下。

1......
2typedef T** map_pointer;
3T* cur;//指向当前元素
4T* first;//指向缓冲区头
5T* last;//指向缓冲区尾巴
6map_pointer node;//二级指针,指向缓冲区地址表中的位置
7......

实现的复杂度可见一斑。正是因为deque复杂的空间结构,其迭代器也想跟着复杂晦涩。于是很容易令人产生异或!

为什么要用这么复杂的空间结构

同学A会疑问:“为什么不直接使用似vector抑或array一个长的数组?这样实现起来简单,而且迭代器也不会像”这个问题很容易被解决,想想:array就不用解释了,因为它是静态的空间,不支持拓展;另外,回想一下,vector在做空间拓展的时候,是如何劳神伤肺?!vector是依从“重新配置,复制,释放”规则,这样的代价是很划不来的。所以宁愿实现复杂的迭代器,来换取宝贵的计算机资源。

那么deque在做空间拓展的时候是如何做的呢?

如果缓冲区中还有备用的空间,那么直接使用备用的空间;否则,另外配置一个缓冲区,将其信息记录到缓冲区地址表里;更有甚者,如果缓冲区地址表都不够的时候,缓冲区地址表也要严格依从“重新配置,复制,释放”规则,但相比对“重新配置,复制,释放”规则宗教式追狂热的vector而言,效率高很多。

deque的创建与遍历

STL中deque有提供多种版本的构造函数,一把使用缺省构造函数。

1......
2deque<int> id;
3......

同样,虽迭代器庞杂,但使用游刃有余,和其他的容器保持一致;并且,迭代器有重载“[]”运算符,所以支持“随机访问”(其实这是假象,详见上述内容)。

01......
02deque<int> id;
03  
04id.push_back(1);
05id.push_back(2);
06id.push_back(3);
07id.push_back(4);
08id.push_back(5);
09id.push_back(6);
10  
11cout << id[2] << endl;  /*3*/
12......

deque的查找

有迭代器在,查找可以用STL<algorithm>内部实现的find()。当然,有重载“[]”运算符,按普通的顺序查找也可行。这里只给出迭代器版本:

01......
02deque<int> id;
03  
04id.push_back(1);
05id.push_back(2);
06id.push_back(6);
07  
08deque<int>::iterator ite;
09  
10ite = find(id.begin(),id.end(),6);
11cout << *ite << endl;   /*6*/
12......

deque的排序

我们已经知道,deque实际不是连续的存储空间,它使用了“分段存储,整体维护”的空间模式,当然代价是庞杂的迭代器。所以STL<algorithm>的sort()函数在这里并不适用。侯杰老师推荐,将deque所有的元素倒腾到一个vector中,再用STL<algorithm>的sort()函数,再从vector中倒腾进deque中。这种折腾是必须的,直接在的deque内部进行排序,效率更低。

建议

deque在实际的应用当中使用的比较少,但正如文章开头指出的,它是容器stack和queue的幕后功臣,所以了解它的内部实现机制多多益善。

 

参考三

/*deque: 是一个double-ended queue,
    1)支持随即存取,也就是[]操作符,
    2)支持两端操作,push(pop)-back(front),在两端操作上与list效率差不多

    因此在实际使用时,如何选择这三个容器中哪一个,应根据你的需要而定,一般应遵循下面的原则: 
    1、如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector 
    2、如果你需要大量的插入和删除,而不关心随即存取,则应使用list 
    3、如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque。
*/

#include <iostream>
#include <deque>
using namespace std;

void printDeque(deque<int> d)
{
//使用下标
//for (unsigned int i = 0; i < d.size(); i++)
//{
// cout<<"d["<<i<<"] = "<<d[i]<<", ";
//}

//使用迭代器
//deque<int>::iterator iter = d.begin();
//for (;iter != d.end(); iter ++)
//{
// cout<<"d["<<iter-d.begin()<<"] = "<<(*iter)<<", ";
//}

//使用迭代器指针
deque<int>::iterator *pIter = new deque<int>::iterator;
if ( NULL == pIter )
{
   return ;
}
for (*pIter = d.begin(); *pIter != d.end(); (*pIter)++)
{
   cout<<"d["<<*pIter - d.begin() <<"]="<<**pIter<<", ";
}
if (NULL != pIter)
{
   delete pIter;
   pIter = NULL;
}

cout<<endl;
}

void main()
{
//创建deque
deque<int> d1; //创建一个没有任何元素的deque对象
deque<int> d2(10);//创建一个具有10个元素的deque对象,每个元素值为默认
deque<double> d3(10, 5.5); //伊妹一个具有10个元素的deque对象,每个元素的初始值为5.5
deque<double> d4(d3); //通过拷贝一个deque对象的元素值, 创建一个新的deque对象
int iArray[] = {11, 13, 19, 23, 27};
deque<int> d5(iArray, iArray+5);//将迭代器区间[first, last)所指的元素拷贝到一个新创建的deque对象中

//初始化赋值:同vector一样,使用尾部插入函数push_back()
for (int i = 1; i < 6 ; i++)
   d1.push_back(i*10);
//遍历元素: 1-下标方式 2-迭代器方式 反向遍历(略)
cout<<"printDeque(d1) : "<<endl;
printDeque(d1);

//元素插入:尾部插入用push_back(),头部插入用push_front(),其它位置插入用insert(&pos, elem)
cout<<"d1.push_front(100): "<<endl;
d1.push_front(100);
printDeque(d1);
cout<<"d1.insert(d1.begin()+3, 200): "<<endl; //支持随机存取(即[]操作符),所以begin()可以+3
d1.insert(d1.begin()+2,200);
printDeque(d1);

//元素删除 尾部删除用pop_back();头部删除用pop_front(); 
//任意迭代位置或迭代区间上的元素删除用erase(&pos)/erase(&first, &last);删除所有元素用clear();
cout<<"d1.pop_front(): "<<endl;
d1.pop_front();
printDeque(d1);

cout<<"d1.erase(d1.begin()+1): "<<endl;
d1.erase(d1.begin()+1); //删除第2个元素d1[1]
printDeque(d1);

cout<<"d1.erase(d1.begin(), d1.begin() + 2) = "<<endl;
d1.erase(d1.begin(), d1.begin() + 2);
printDeque(d1);

cout<<"d1.clear() :"<<endl;
d1.clear();
printDeque(d1);


//其它常用
cout<<"其它常用用法: "<<endl;
int flag = 0;
while(flag < 2)
{
   if (0 == flag )
   {
    for (int i = 1; i < 6 ; i++) //恢复
     d1.push_back(i*10);
   }
   else
   {
    d1.clear();
    cout<<"after d1.clear() , d1.front(), d1.back() is abnormal! other info.:"<<endl;
   }
   cout<<"d1.empty() = "<<d1.empty()<<endl;
   cout<<"d1.size() = "<<d1.size()<<endl;
   cout<<"d1.max_size() = "<<hex<<d1.max_size()<<endl;
   if (!d1.empty())
   {
    cout<<"d1.front() = "<<d1.front()<<endl;
    cout<<"d1.back() = "<<d1.back()<<endl;
   }
  
   flag++;
  
}

//交换
cout<<"d1.swap(d5)= "<<endl;
d1.swap(d5);
cout<<"d1 = ";
printDeque(d1);
cout<<"d5 = ";
printDeque(d5);
//printDeque(d)


}


 

参考四

[STL] DEQUE (双端队列)

STL :: DEQUE

 
deque双端队列容器(double-ended queue)与vector非常相似,算法的时间复杂度也是常数阶O(1),deque内部的数据机制和执行性能与vector不同,一般说来,当考虑到容器元素的内存分配策略和操作的性能时,deque相对vector较为有优势。deque双端队列采用分块的线性结构来存储数据,具有高效的删除首尾元素的函数,由于deque容器是以deque块为单位进行内存的分配,并使用了二级的Map进行管理,因此不容易实现类似于vector的capacity和reverse函数,而且deque容器也不需要这样的获取和调整容器大小的函数。
 
 
Deque应用基础:

 
创建deque对象:
 
1、deque() 创建一个空的deque对象。
 
如:deque<int> d;
 
2、deque(size_type n) 创建一个具有n个元素的deque对象,每个deque元素具有它的类型下的默认值。
 
如:deque<int> d(10);
 
3、deque(size_type n, const T& value) 创建一个具有n个元素的deque对象,每个元素具有初始值value。
 
如:deque<int> d(10, 5);
 
4、deque(const deque&) 通过拷贝一个deque对象的各个元素值,创建一个新的deque对象。
 
如:deque<int> d1(10, 5);        deque<int> d2(d1);
 
5、deque(const InputIterator first, const InputIterator last, const A& a=A()) 通过拷贝迭代器区间[first, last)的元素值,创建一个新的deque对象中,内存分配器可省略。
 
如:int iArray[] = {1,2,3};      deque<int> d(iArray, iArray+3);
 
 
初始化赋值:
 
deque提供的push_back函数常用来进行deque容器的初始化,push_back函数在容器的尾端插入新元素value。
 
void push_back(const T&)
 
元素的遍历访问:
 
deque的元素可采用数组或迭代器的方式进行遍历访问。
 
元素的插入:
 
由于deque使用了两个迭代器分别指向双端队列的首尾,因此deque具有高效的头部插入元素的函数push_front()
 
void push_front(const T&)
 
其它位置的插入,将涉及相关元素的移位拷贝,insert函数的一个较常用的原型为:
 
iterator insert(iterator pos, const T& x)
 
元素的删除:
 
void pop_front() 删除deque的第一个元素
 
void pop_back() 删除deque的最后一个元素
 
iterator erase(iterator pos) 删除迭代器pos所指的元素
 
iterator erase(iterator first, iterator last) 删除迭代器区间[first, last)的所有元素
 
void clear() 调用erase函数,清除所有元素
 
元素的反向遍历:
 
reverse_iterator rbegin()
 
reverse_iterator rend()
 
deque的交换:
 
void swap(deque&)
 
其它常用函数:
 
bool empty()
 
size_type size()
 
size_type max_size()
 
reference front()
 
reference back()
 
 
 
举例分析:

 
 
1、
 
//用数组的方式访问deque元素
#include <iostream>
#include <deque>
using namespace std;
int main(void)
{
deque<int> d;
d.push_back(13);
d.push_back(32);
d.push_back(29);
for (int i=0; i<d.size(); i++)
{
   cout << "d[" << i << "] = " << d[i] << endl;
}
 
return 0;
}
 
2、
 
 
//用迭代器访问deque元素
#include <deque>
#include <iostream>
using namespace std;
 
int main(void)
{
deque<string> d;
d.push_back("a");
d.push_back("b");
d.push_back("c");
deque<string>::iterator i, iend;
iend = d.end();
int j;
for (i=d.begin(),j=0; i!=iend; i++,j++)
{
   cout << *i << " ";
}
cout << endl;
 
return 0;
}
 
3、
 
 
//头部和中间位置插入deque元素
#include <deque>
#include <iostream>
using namespace std;
 
int main(void)
{
deque<int> d;
d.push_back(6);
d.push_back(7);   
//头部插入
d.push_front(5);
for (int i=0; i<d.size(); i++)
{
   cout << d[i] << " ";
}
cout << endl;
 
//中间位置插入
d.insert(d.begin()+1, 9);
for (int j=0; j<d.size(); j++)
{
   cout << d[j] << ' ';
}
cout << endl;
 
return 0;
}
 
4、
 
 
//头尾和其它位置删除deque元素
#include <deque>
#include <iostream>
using namespace std;
 
int main(void)
{
deque<int> d;
d.push_back(4);
d.push_back(5);
d.push_back(3);
d.push_back(3);
d.push_back(3);
d.push_back(6);
for (int i=0; i<d.size(); i++)
{
   cout << d[i] << ' ' ;
}
cout << endl;
 
//头尾和任意位置删除元素
d.erase(d.begin()+1);
d.pop_front();
d.pop_back();
for (int j=0; j<d.size(); j++)
{
   cout << d[j] << ' ' ;
}
cout << endl;
 
//删除所有元素
d.clear();
cout << "执行clear()" << endl << "deque元素全部清除" << endl;
 
return 0;
}
 
5、
 
 
//deque元素的反向遍历
#include <deque>
#include <iostream>
using namespace std;
 
int main(void)
{
deque<int> d;
d.push_back(1);    
d.push_back(3);
d.push_back(5);
d.push_back(7);
d.push_back(9);
 
//deque元素的前向遍历
deque<int>::iterator i, iend;
iend = d.end();
for (i=d.begin(); i!=iend; i++)
{
   cout << *i << " ";
}
cout << endl;
 
//deque元素的反向遍历
deque<int>::reverse_iterator ri, riend;
riend = d.rend();
for (ri=d.rbegin(); ri!=riend; ri++)
{
   cout << *ri << " ";
}
cout << endl;
 
return 0;
 
6、
 
//两个deque容器的元素交换
#include <deque>
#include <iostream>
using namespace std;
 
void print(deque<int>& d)
{
for (int i=0; i<d.size(); i++)
{
   cout << d[i] << " ";
}
cout << endl;
}
 
int main(void)
{
deque<int> d1;
d1.push_back(11);
d1.push_back(12); 
d1.push_back(13);
cout << "d1 = ";
print(d1);
 
deque<int> d2;
d2.push_back(90);
d2.push_back(91);
d2.push_back(92);
cout << "d2 = ";
print(d2);
 
//d1和d2交换
d1.swap(d2);
cout << "d1与d2交换后" << endl;
cout << "d1 = ";
print(d1);
cout << "d2 = ";
print(d2);
 
return 0;
}
 
7、
 
 
//deque其它常用函数的使用
#include <deque>
#include <iostream>
using namespace std;
 
int main(void)
{
deque<string> d;
//打印deque为空
cout << "d是否为空: " << d.empty() << endl;
//装入元素
d.push_back("红楼梦");
d.push_back("源氏物语");
d.push_back("教父");
d.push_back("水浒传");
d.push_back("24史");
 
//打印deque所有元素
deque<string>::iterator i, iend;
iend = d.end();
for (i=d.begin(); i!=iend; i++)
{
   cout << *i << " ";
}
cout << endl;
 
//打印首元素
cout << "deque首元素为: " << d.front() << endl;
//打印末元素
cout << "deque末元素为: " << d.back() << endl;
//打印元素个数
cout << "deque元素个数为: " << d.size() << endl;
//打印可支持的最大deque元素个数
cout << "deque最大元素个数为: " << d.max_size() << endl;
 
return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值