顺序容器
vector
vector底层是一个可以动态扩容的数组
提供的方法有
push_back() 尾部插入
pop_back() 尾部删除
insert() 插入一个元素
erase() 删除一个元素
begin() 返回第一个元素的迭代器
end() 返回最末元素的迭代器( )
swap() 交换两个Vector
resize() 改变Vector元素数量的大小
reserve() 设置Vector最小的元素容纳数量
#include <iostream>
using namespace std;
template<typename T>
class Vector
{
public:
~Vector()
{
delete []mpVec;
}
// 按指定size进行构造,size个空间,没有元素
Vector(int size = 0):mSize(size),mCur(0)
{
if(size != 0)
{
mpVec = new T[size];
}
}
// 按指定size进行构造,添加size个元素,元素值是val
Vector(int size, const T &val = T()):mSize(size),mCur(size)
{
mpVec = new T[size];
for(int i = 0; i < size; i++)
{
mpVec[i] = val;
}
}
// 按[first, last)区间的元素来构造Vector
Vector(T *first, T *last)
{
mCur = 0;
mpVec = new T[last-first];
mSize = last-first;
T *p = first;
for(; p != last; p++)
{
mpVec[mCur++] = *p;
}
}
// 从末尾添加元素
void push_back(const T &val)
{
if(full())
{
resize();
}
mpVec[mCur++] = val;
}
// 从末尾删除元素
void pop_back()
{
if(empty())
{
return ;
}
--mCur;
}
bool full()const
{
return mCur == mSize;
}
bool empty()const
{
return mCur == 0;
}
// 返回容器元素的个数
int size()const
{
return mCur;
}
// Vector的迭代器
class iterator
{
public:
iterator(T *p):_iter(p){}
bool operator != (const iterator &val)
{
return _iter != val._iter;
}
T& operator *()
{
return *_iter;
}
void operator++ ()
{
++_iter;
}
private:
T *_iter;
};
iterator begin()
{
return iterator(mpVec);
}
iterator end()
{
return iterator(mpVec+mCur);
}
private:
T *mpVec;
int mCur;
int mSize;
// 容器内存2倍扩容
void resize()
{
if(mSize != 0)
{
T *newdata = new T[mSize*2];
for(int i = 0; i < mCur; i++)
{
newdata[i] = mpVec[i];
}
delete []mpVec;
mpVec = newdata;
mSize *= 2;
}
else
{
mpVec = new T[1];
mSize = 1;
}
}
};
list
list 链式容器
内部数据结构是一个双向链表,不考虑查找的情况下,插入和删除一个元素的时间复杂度都是O(1),查找的时间复杂度为O(n),我们在代码中看一下list 提供的各个方法
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> li;
for(int i = 0; i < 10; i++)
{
//list 中 头插法的时间复杂度为O(1)
li.push_front(i);
}
//如果采用push_back() 时间复杂度为O(n)
li.push_back(10);
//在进行两个list 合并的时候,将两个list 内的数据拷贝一份
//然后放到新的list中的过程效率较低,如果合并后两个list 不再使用,
//可以 将两个list中的节点摘下来放到新的list中,这样就少去了大量的
//拷贝构造的过程,list 中提供了splice 方法
list<int> li2;
/*
void splice( iterator pos, list &lst );
void splice( iterator pos, list &lst, iterator del );
void splice( iterator pos, list &lst, iterator start, iterator end );
*/
li2.splice(li2.begin(),li,li.begin(),li.end);
/*
merge()函数把自己和lst链表连接在一起,
产生一个整齐排列的组合链表。如果指定compfunction,则将指定函数作为比较的依据。
void merge( list &lst );
void merge( list &lst, Comp compfunction );
*/
li2.merge(li);
return 0;
}
其余方法与vector 这里不多做介绍
deque
deque的底层数据结构
first 与last 的初始位置在其中一个数组的中间位置,这样,不管在头部还是尾部的的插入与删除的时间复杂度都是O(1)
deque开始的数组的长度4096/sizeof(T)。
双端队列扩容方式,先扩容一维数组MAP_SIZE 扩容后的将现在的数组放在扩容后数组的中间,如下图所示
vector 与 deque 的insert 谁的效率高?
vector 内存是连续的,deque 内存是不连续的, insert的移动元素的过程连续的内存较快。所以vector 效率高
容器适配器
容器适配器的底层都是用的已知的容器,它没有自己的数据结构和迭代器
stack
底层通过一个deque容器实现
stack为什么不采用vector作为底层数据结构?
deque 的底层内存空间不连续,内存的空间利用率高。Vector 初始值为0,开始的开始时扩容的次数较大 deque开始的数组的长度4096/sizeof(T);所以开始时deque 的效率较高
提供的方法
empty() 堆栈为空则返回真
pop() 移除栈顶元素
push() 在栈顶增加元素
size() 返回栈中元素数目
top() 返回栈顶元素
queue
queue的底层容器的实现也是deque
queue不采用vector作为底层结构的原因?
queue的出队操作若采用vector的删除第一个元素的话时间复杂度为O(n),采用deque 的时间复杂度为O(1),
queue头文件中可以看出queue 的底层默认使用的容器
template<class _Ty,
class _Container = deque<_Ty> >
常用的方法有:
back() 返回最后一个元素
empty() 如果队列空则返回真
front() 返回第一个元素
pop() 删除第一个元素
push() 在末尾加入一个元素
size() 返回队列中元素的个数
priority_queue
优先级队列,先来看一下他的头文件
template<class _Ty,
class _Container = vector<_Ty>,
class _Pr = less<typename _Container::value_type> >
Priority_queue底层采用的是vector数据结构,默认情况下,priority_queue内部采用大根堆存储,构建根堆的过程使用了大量的下标访问,vector 数据结构使用是最方便使用下标访问的。
优先级队列在容器的内部并不是有序的,而是通过大根堆(小根堆),在用户访问数据的时候,显示出有序的状态,因为每出队一个(入队)一个元素,都会将优先级最高的放到堆顶,这样,我们一个个访问的时候看到的数据就是有序的。
priority_queue的第三个参数是函数对象,所谓函数对象是指一个类中,提供小括号运算符重载函数,这样的类构造的一个对象
class comp
{
public:
bool operator()(int a,int b)
{
return a > b;
}
};
这样一个类构成的对象就是函数对象