list
-数据结构
(1)环装双向链表,只需要一个指针,可以完整表现整个链表
(2)只要刻意在环状链表的尾端加上一个空白节点,便可满足“前闭后开”区间。
-
空间分配
(1)非连续存储空间,容量大小=元素个数;
(2)每次配置一个节点的空间,当元素删除时,相应空间一并删除; -
迭代器
(1)因为节点不连续保存在存储空间,不可以使用普通指针做迭代器;
(2)双向链表,双向迭代器;
(3)插入操作和接合操作都不会造成原有的list迭代器失效,只有在删除元素时,“指向被删除元素”的那个迭代器失效,其他迭代器不受影响 -
插入
分配一个节点,位于目前所指迭代器之前
-
删除
删除元素,释放空间
-
优缺点
优点:无预留空间。插入和删除在常数时间内完成
缺点:不能进行随机存取。时间复杂度为O(N)。 -
外部接口
#include <list>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
std::list<int> iList;
std::cout<<"size="<<iList.size()<<std::endl;//0
iList.push_back(0);
iList.push_back(1);
iList.push_back(2);
iList.push_back(3);
iList.push_back(4);
std::cout<<"size="<<iList.size()<<std::endl;//5
std::list<int>::iterator it;
for(it=iList.begin();it!=iList.end();++it) cout<<*it<<'';//01234
it=find(iList.begin(),iList.end(),3);
if(it!=0) iList.insert(it,99);
cout<<*it<<endl;//3 迭代器依然指向3,是在3前面插入
for(it=iList.begin();it!=iList.end();++it) cout<<*it<<'';//0129934
it=find(iList.begin(),iList.end(),1);
if(it!=0) cout<<*(iList.erase(it))<<endl;//2
}
- 相关方法
// 申请一个节点
link_type get_node() { return list_node_allocator::allocate(); }
// 释放一个节点
void put_node(link_type p) { list_node_allocator::deallocate(p); }
// 创建一个节点并初始化
link_type create_node(const T& x)
{
link_type p = get_node();
construct(&p->data, x); //
return p;
}
// 删除一个节点
void destroy_node(link_type p)
{
destroy(&p->data); //
put_node(p);
}
// 无参构造函数
list() { empty_initialize(); }
void empty_initialize()
{
node = get_node();
node->next = node;
node->prev = node;
}
// 返回头结点
iterator begin() { return (link_type)((*node).next); }
// 放回尾节点
iterator end() { return node; }
// 判断链表是否为空
bool empty() const { return node->next == node; }
// 计算链表的大小
size_type size() const
{
size_type result = 0;
distance(begin(), end(), result);
return result;
}
// 返回头文件的数据
reference front() { return *begin(); }
// 返回尾节点的数据
reference back() { return *(--end()); }
// 尾部插入数据
void push_back(const T& x) { insert(end(), x); }
// 头部插入数据
void push_front(const T& x) {insert(begin(), x);}
// 删除头部节点
void pop_front() { erase(begin()); }
// 删除尾节点
void pop_back()
{
iterator tmp = end();
erase(--tmp);
}
// 在position前面插入一个节点,数据为x 先处理待插入的节点,再处理前后节点
iterator insert(iterator position, const T& x)
{
link_type tmp = create_node(x);
tmp->next = position.node;
tmp->prev = position.node->prev;
(link_type(position.node->prev))->next = tmp;
position.node->prev = tmp;
return tmp;
}
// 删除指定位置的节点
iterator erase(iterator position)
{
link_type next_node = link_type(position.node->next);
link_type prev_node = link_type(position.node->prev);
prev_node->next = next_node;
next_node->prev = prev_node;
destroy_node(position.node);
return iterator(next_node);
}
// 清空链表
template <class T, class Alloc>
void list<T, Alloc>::clear()
{
link_type cur = (link_type) node->next;
while (cur != node)
{
link_type tmp = cur;
cur = (link_type) cur->next;
destroy_node(tmp);
}
node->next = node;
node->prev = node;
}
// 将数据为value的节点全部删除
template <class T, class Alloc>
void list<T, Alloc>::remove(const T& value)
{
iterator first = begin();
iterator last = end();
while (first != last)
{
iterator next = first;
++next;
if (*first == value)
erase(first);
first = next;
}
}
deque
-
特点
(1)vector是单向开口连续线性空间,deque是双向开口连续线性空间,允许在头尾进行插入删除操作,vector也可以但是在头部操作效率低;
(2)deque前后开口,没有容量的概念;
(3)deque是由一段一段的定量连续空间构成,利用迭代器实现“整体连续”的假象;
(4)利用中控器(MAP)实现从一个缓冲区跳跃到下一个缓冲区; -
中控器
(1)deque采用一块所谓的map(不是map容器)作为主控。这里所谓的map是一小块连续空间,其中每个元素都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的存储空间主体。
(2)deque的连续空间其实是假象,是由一段一段的定量连续空间构成
(3)Map是T,也就是说他是一个指针,所指之物又是一个指针,指向型别为T的一块空间**。
-
迭代器
(1)deque是分段连续,迭代器要实现其“整体连续”的假象。迭代器要指出分段连续空间(缓冲区)在哪里,并且判断是否处于缓冲区的边缘,如果是就要跳跃到下一个或者上一个缓冲区,deque必须随时掌握管控中心(map)
(2)deque的迭代器包含四个指针
cur:指向缓冲区的现行元素
first:指向缓冲区的头部
last:指向缓冲区的尾部
**node:**指向掌控中心MAP
-
构造及内部原理
在前端、后端添加元素,要考虑是否需要新开辟一个缓冲区;
在前端、后端删除元素,要考虑是否要是否一个缓冲区。
具体添加删除原理,详见<STL源码剖析>P185
以find()函数为例,
std::deque<int> MyDeque(5,0);//构造,初始化为5个0
for(int i=0;i<MyDeque.size();i++)
MyDeque[i]=i+1;//重新赋值,因为是连续性容器,所以允许这样赋值,类似vector
MyDeque.push_back(7);//队尾添加
MyDeque.push_front(99);//队首添加
std::deque<int>::iterator itd;//迭代器
itd=find(MyDeque.begin(),MyDeque.end(),99);//find()返回一个迭代器,99前端的位置
cout<<*itd<<endl;//99
cout<<*(itd.cur)<<endl;//99
begin()返回start迭代器;end()返回finish迭代器。
stack(堆栈容器)
- 特点:
(1)stack是一种先进后出的数据结构;
(2)只能对栈顶元素进行操作,只能在栈顶新增元素、移除、获得最顶端元素,不接受遍历行为,因此没有迭代器;
(3)以双向开口的deque和list作为底部结构,例如将deque为底部结构并封闭其底端开口,形成stack, deque 作为缺省情况下的 stack 底部结构。因为stack是以底部容器完成所有工作,并且具有“修改某物接口,形成另一种风貌”的特点,是一种配接器,一般在STL中被归类为容器配接器。
-源码
#ifndef __SGI_STL_INTERNAL_STACK_H
#define __SGI_STL_INTERNAL_STACK_H
__STL_BEGIN_NAMESPACE
// 如果编译器不能根据前面模板参数推导出后面使用的默认参数类型,
// 那么就需要手工指定, 本实作stack内部容器默认使用deque
// 选用deque可以在存储空间不足时可以动态增加, 而且代价很低
#ifndef __STL_LIMITED_DEFAULT_TEMPLATES
template <class T, class Sequence = deque<T> >
#else
template <class T, class Sequence>
#endif
class stack
{
// 特化的全局运算符, 提供operator==和<重载则构建出所有运算符
// 其具体细节见<stl_pair.h>中的说明
friend bool operator== __STL_NULL_TMPL_ARGS (const stack&, const stack&);
friend bool operator< __STL_NULL_TMPL_ARGS (const stack&, const stack&);
public:
// 由于stack仅支持对栈顶元素的操作, 所以不定义STL要求的
// pointer, iterator, difference_type
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
protected:
Sequence c; // 这个是我们实际维护的容器
public:
// 下面的操作完全使用内部容器的成员函数实现
// 这再次体现了STL高度的可复用性:-)
// 判断stack是否为空
bool empty() const { return c.empty(); }
// stack中元素个数
size_type size() const { return c.size(); }
// 返回栈顶元素, 注意这里返回的是引用!!!
reference top() { return c.back(); }
const_reference top() const { return c.back(); }
// 在栈顶追加新元素
void push(const value_type& x) { c.push_back(x); }
// 移除栈顶元素, 注意不返回元素的引用,
// 很多初学者随机用此容器时经常误认为pop()操作同时会返回栈顶元素的引用
void pop() { c.pop_back(); }
};
// 判断两个stack是否相等, 就要测试其内部维护容器是否相等
// x.c == y.c会调用容器重载的operator ==
template <class T, class Sequence>
bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y)
{
return x.c == y.c;
}
template <class T, class Sequence>
bool operator<(const stack<T, Sequence>& x, const stack<T, Sequence>& y)
{
return x.c < y.c;
}
__STL_END_NAMESPACE
#endif /* __SGI_STL_INTERNAL_STACK_H */
- 外部接口
#include<stack>
#include<iostream>
using namespace std;
int main()
{
stack<int> MyStack;//创建堆栈对象 型别可以是:int float string 或者指针、自己定义的类型
MyStack.push(1);
MyStack.push(2);
MyStack.push(3);//元素入栈,从顶端入栈
while(!MyStack.empty())//如果容器为空,MyStack.empty()返回true
{
std::cout<<MyStack.top()<<std::endl;//打印出321,top只负责显示值,不能弹出
MyStack.pop();//弹出栈顶元素。pop只负责弹出,没有返回值
}
stack<int> MyStackB(MyStack);//拷贝构造
stack<int> MyStackC=MyStack;//赋值
return 0;
}
queue
- 特点
(1)queue是一种先进先出的容器;
(2)有一个入口一个出口,允许在队尾加入数据,队头提取数据,不允许遍历操作,没有迭代器,类似于排队。
(3)以deque为底层实现,和stack一起分类为容器配接器。
- 外部接口
#include<queue>
#include<iostream>
using namespace std;
int main()
{
queue<int> MyQueue;
MyQueue.push(1);
MyQueue.push(2);//从队尾添加元素
cout<<MyQueue.back()<<endl;//返回队尾元素
while(!MyQueue.empty())
{
cout<<MyQueue.front()<<endl;//返回队首元素
MyQueue.pop();//弹出队首元素没有返回值
}
return 0;
}