STL数据结构容器总结
1.序列式容器
Vector
vector的操作与array很像,唯一差别在于空间运用的灵活性。array是静态空间,vector是动态空间。
vector的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率。为了降低空间配置时的速度成本,实际配置的容量大小永远大于或者等于vector大小(vector.size())。当我们push_back()插入一个新元素时,先判断是否有备用空间,有就直接在备用空间上构造函数,并调整迭代器finish。如果没有就扩充空间(重新配置、移动数据、释放原空间)。
空间配置原则:如果原大小为0,则配置1.如果原大小不为0,则配置为原大小的两倍。前半段用来存放原数据,后半段用来存放新数据。
注意,所谓动态增加大小,不是在原空间之后接连续空间(无法保证原空间后还有可供配置的空间),而是以原大小的两倍另外配置一块较大空间,然后将原数据拷贝至新空间,然后才开始在原内容后构造新元素,并释放原空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原空间的所有迭代器就失效了(vector的迭代器是普通指针)。
例子:
1、Ducci序列
对于一个n元组(a1,a2,…,an),可以对每个数求出它和下一个数的差的绝对值,得到一个新的n元组(|a1-a2|,|a2-a3|,…,|an-a1|),重复这个过程得到的序列称为Ducci序列。例如;(8,11,2,7)->(3,9,5,1)->(6,4,4,2)->(2,0,2,4)->(2,2,2,2)->(0,0,0,0).
也有的Ducci序列会一直循环下去。现在输入一个n元组(3<n<15),判断它最终会循环还是变成0.
<分析>
序列可以直接使用vector来表示,vector本身已经重载了等号运算符对两个容器的内容进行比较。直接使用“==”操作符即可。判断是否重复使用set<vector>
using namespace std
#define _for(i,a,b) for( int i=(a); i<(b) ; ++i)
int readint() { int x; scanf("%d",&x); return x; }
int main(){
int T = readint();
vector<int> seq ,zeroseq;
set< vector<int> > seqs;
while(T--){
int n =readint();
seq.clear(); zeroseq.resize(n);
_for(i, 0, n) seq.push_back( readint() );
bool zero = false; bool loop = false;
seqs.clear(), seqs.insert(seq);
do{
if(seq == zeroseq) { puts(" LOOP") ; break;}
int a0 = seq[0];
_for(i,0,n) {
if (i == n-1) seq[i] = abs(seq[i] - a0);
else seq[i] = abs( seq[i]-seq[i+1] );
}
if (seqs.count(seq)) { puts("LOOP"); break;} // seqs.count(seq)表示seq如果在seqs里已经存在则返回true;
seqs.insert(seq);
}while(true)
}
return 0;
}
List
list不能再像vector一样用普通指针作为迭代器,因为其节点不能保证在存储空间中连续存在。
List有一个重要性质:插入(insert)操作和接合(splice)操作都不会造成原有的list迭代器失效。这在vector里面是不成立的,因为vector的插入操作可能造成记忆体重新配置,导致原有的迭代器全部失效。甚至list的元素删除(erase)操作,也只有“指向被删除元素”的迭代器失效,其他迭代器不受影响。
STL list 的节点node结构
template <class T>
struct _list_node{
typedef void* void_pointer;
void_pointer prev; //型别为void*,其实也可以设为 _list_node<T>*
void_pointer next;
T data;
}
插入操作完成后,新节点将位于哨兵迭代器(标示初插入点)所指节点的前方——这是STL对于插入操作的标准规范。
当我们push_back()将新元素插入到list尾端时,此函数内部调用了insert();
void push_back() (const T& x) { insert(end(),x);}
insert()是一个重载函数,有多种形式,其中一种简单的形式如下:首先配置并构造一个节点,然后在尾端进行适当的指针操作,将新节点插入进去。
//函数目的:在迭代器position所指位置插入一个节点,内容为x
iterator insert( iterator position, const T& x){
link_type tmp = creat_node(x);
tmp ->next =position.node;
tmp->prev = position.node->prev;
(link_type(position.node->prev))->next = tmp;
position.node ->prev = tmp;
return tmp;
}
deque
vector 是单向开口的连续线性空间,deque则是一种双向开口的连续分段线性空间。vector从技术角度来说也可以在头尾两端进行操作,但是其头部操作效率