数据结构 -- C++ STL中的数据结构与算法[1]
数据结构 -- C++ STL中的数据结构与算法[1]
什么是 STL
STL是Standard Template Library的简称,中文名标准模板库,惠普实验室开发的一系列软件的统称。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普实验室工作时所开发出来的。从根本上说,STL是一些“容器”与“算法”的集合,所谓的这些“容器”无非就是已经实现好了数据结构,能够让程序设计者更为方便的进行调用,“算法”则顾名思义就是已预先实现好了的算法集合。
STL的目的是标准化组件,这样就不用重新开发,可以使用现成的组件。STL现在是C++的一部分,因此不用安装额外的库文件。STL的版本很多,有很多公司或者工作室自定义STL形成各种各样的自定义标准。。
在C++标准中,STL被组织为下面的13个头文件:<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack>和<utility>。
STL中包含的几大内容:
1.容器(Container)
是一种数据结构,也是本章节提的重点,如list(链表),vector(向量数组),stack(栈),队列(queue) ,以模板类的方法提供,为了访问容器中的数据,可以使用由容器类输出的迭代器。
- 迭代器(Iterator)
是一种特殊的指针,它提供了访问容器中对象的方法,在程序设计中,它扮演了容器和算法之间的胶合剂,利用迭代器可以快速而安全的对容器内容进行操作,或是进行算法模板的使用。
- 算法(Algorithm)
(部分书籍称为泛型算法,generic algorithms),是一类常用的算法模板,既可以对容器进行操作,同时其开放性也让算法类本身可以针对数组或者是自定义结构体等结构进行直接的操作。
- *仿函数(Function object)(又称为函数对象,function object)
是一种行为类似函数,这样讲可能有些抽象,我们可以理解为一种高级的,重载了()操作符的结构体与类。
5.*迭代适配器(Iterator Adaptor)
是一种用来修饰容器或者仿函数的接口,它使得得带适配器使算法能够以逆向模式,安插模式进行工作,甚至还可以与流配合,它对容器起到非常大的辅助作用,同时他还将迭代器进行了更高级别的抽象。
- *空间配制器(allocator)
是负责空间的配置与管理,重点就是对容器的空间申请和空间释放进行管理,你可以理解为C的malloc和free函数,C++的new和delete关键字。
Vector容器
1. 概念
Vector可以翻译为向量,或向量数组,至于为什么以向量命名,可以理解为一维空间也是存在向量的。
Vector是最简单的序列是容器,就像数组一样,向量使用连续的存储位置作为元素,这意味着它们的元素也可以使用常量指向其元素的偏移来访问,与数组一样有效。但与数组不同,它们的大小可以动态变化,其存储由容器自动处理。
总结一下Vector就是一个动态创建空间,且预先加载了常用的数组操作的数组
2. 相关文件
头文件:#include <vector>
3. 初始化
格式为:vector<Data_Types> name;
我们以int类型作为参数为例,进行创建。
vector<int> v1; //创建一个空的向量v1
vector<int> v2(10); //创建一个向量v2,其已开辟10个元素的空间,相当于int v[10];
vector<int> v3(10,5); //创建一个向量v3,其已开辟10个元素的空间并全部赋值为5
vector<int> v4(v3.begin(),v3.end()); //创建一个向量v3,其内容为向量v3的内容
vector<int> v5(v4); //创建一个向量v5,其包含了v4的全部内容
4. 迭代器
顾名思义,迭代器是一种安全的访问控制器,它本身是一种指针,用于直接的元素访问。其遍历访问的大致思路是,创建容器的迭代器,让迭代器指向第一个元素,逐步向后移动并最终指向最后一个元素结束。
遍历代码举例:
vector<int> v; //创建一个向量vs
vector<int>::iterator it; //C98标准
for(it=v.begin();it!=v.end();it++){
cout<<*it<<' ';
}
当然,遍历也可以直接使用下标访问:
for(int i=0;i<v.size();i++){
cout<<v[i]<<' ';
}
5.常用接口(常用的)
Vector的接口有非常多,不同的C++语言标准也不同,这里只提供一些常用的进行简介,具体的使用可以翻阅官方文档(英文),或是别人的翻译稿。
我们使用 vector<int> v; 预先创建了一个向量。
a) 向量尾插入push_back()
在向量的末尾添加一个新元素val,并自动让容器大小增大一个。
函数原型:
void push_back (const value_type& val);
使用举例:
v.push_back(10); //插入一个数据10
b) 向量尾删除pop_back()
移除向量尾的最后一个元素,并且将容器大小减小一个,
函数原型:void pop_back();
使用举例:
v.pop_back();
c) 插入insert()
插入元素到指定位置,通过在元素之前在指定位置插入新元素来扩展向量,从而有效地增加容器大小所插入的元素数量。
函数原型:
插入单一数据到指定位置:
iterator insert (iterator position, const value_type& val);
插入一段数据到指定位置:
void insert (iterator position, size_type n, const value_type& val);
插入一段别的容器的数据到指定位置:
*template *
void insert (iterator position, InputIterator first, InputIterator last);
使用举例:
v.insert(v.begin(),10); //在向量最前端插入数据10
v.insert(v.begin(),5,20); //在向量最前端插入5个数据20
vector<int> k(2,50); //创建一个新的向量k,其拥有2个元素内容均为50
v.insert(v.begin(),k.begin(),k.end()); //在向量v最前端插入向量K的全部内容
d) 删除erase()
删除一个元素,或者是一段区间的元素,将会自动缩减空间使用。
函数原型:
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
使用举例
v.erase(v.begin()); //删除第一个元素
v.erase(v.begin(),v.begin()+4); //删除从第一个开始后4个元素(包括第一个)
List容器
1.概念
链表是线性表 ,STL链表是单链表的形式。
2.头文件
头文件:#include <list>
3.初始化
格式为:explicit list (const allocator_type& alloc = allocator_type());
我们以int类型作为参数为例进行创建,其创建方法与vector无异
list<int> l1; //创建一个空链表
list<int> l2(10); //创建一个链表其有10个空元素
list<int> l3(5,20); //创建一个链表其有5个元素内容为20
list<int> l4(l3.begin(),l3.end()); //创建一个链表其内容为l3的内容
list<int> l5(l4); //创建一个链表其内容为l4的内容
4. 迭代器
遍历代码举例(其方法和vector版本无异只是更加精简):
list<int> li;
for(list<int>::iterator it=li.begin();it!=li.end();it++){
cout<<*it<<' ';
}
5. 常用接口
a)判断是否为空empty()
返回一个bool类型的值,只存在真和假,当链表为空时为真,不为空时为假
函数原型
bool empty() const;
b)获取大小size()
返回链表元素的个数
函数原型
size_type size() const;
c) 链表前插入push_front() &&删除 pop_front()
push_front()表示在链表最前端插入一个数据,pop_front()表示在链表最前端删除一个数据。
函数原型
void push_front (const value_type& val);
void pop_front();
d) 链表后插入push_back() &&删除 pop_back()
push_back()表示在链表尾插入一个数据,pop_back()表示将链表尾删除一个数据。
函数原型:
void push_back (const value_type& val);
void pop_back();
e) 插入insert()
插入元素到指定位置,通过在元素之前在指定位置插入新元素来扩展向量,从而有效地增加容器大小所插入的元素数量。
函数原型:
插入单一数据到指定位置:
iterator insert (iterator position, const value_type& val);
插入一段数据到指定位置:
void insert (iterator position, size_type n, const value_type& val);
插入一段别的容器的数据到指定位置:
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
f) 删除erase()
删除一个元素,或者是一段区间的元素,将会自动缩减空间使用。
函数原型:
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
stack栈容器
1. 栈
回顾一下之前所学的栈,栈是一种先进后出的数据结构,而实现方式需要创建多个结构体,通过链式的方式进行实现,这是标准的栈的思路,而在STL中栈可以以更为简单的方式实现。
2. 头文件
头文件 #include<stack>
3. 初始化
格式为:explicit stack (const container_type& ctnr = container_type());
我们以int类型作为参数为例进行创建,其创建方法与vector无异
stack<int> s;
stack<int> v(s);
标准的栈创建方法是直接创建空栈,由于栈的特殊性质,让他拥有其他容器的参数可以这样创建,这种多参数的方式可能有一些复杂,一般也很少这样使用。
vector<int> v(3,100);
stack<int,vector<int> > s(v); //注意,> >符号之间需要有一个空格隔开
通过标准的方式创建向量数组,然后通过复制构造函数的方式进行创建,其内容就是vector数组的全部内容。
4. 迭代器
栈和队列都属于一种特殊的数据结构,只能通过访问顶层数据并不断剔除数据的方法进行全部访问,因此没有直接的迭代器。
5. 常用接口
我们使用stack<int> s 预先创建了一个栈,命名为s,方便举例
a)大小size()
返回链表元素的个数
函数原型:size_type size() const;
b) 返回栈顶元素top()
返回栈顶元素内容
函数原型:
reference& top();
const_reference& top() const;
c) 入栈push()
往栈顶中插入一个元素。
函数原型: void push (const value_type& val);
d)出栈pop()
将栈顶元素释放,注意pop()函数是没有返回值的,如果要想访问后删除需要先top再pop使用。
函数原型:void pop();
s.pop();
e) 判空empty()
返回一个bool类型的值,只存在真和假,当栈为空时为真,不为空时为假
函数原型
bool empty() const;
Queue容器
1. 再谈队列
回顾一下之前所学的队列,队列和栈不同,队列是一种先进先出的数据结构,STL的队列内容极其重要,虽然内容较少但是请务必掌握,STL的队列是快速构建搜索算法以及相关的数论图论的状态存储的基础。
2.相关文件
头文件:#include<queue>
3.初始化
格式为:
explicit queue (const container_type& ctnr = container_type());
我们以int类型作为参数为例进行创建。
queue<int> q; //创建一个空的没有数据的队列q
queue<int> qoo(q); //创建一个队列其元素为q的全部内容
标准的队列创建方法是直接创建空队列再进行其他的操作,由于队列的特殊性质,拥有其他容器的参数可以这样创建,这种多参数的方式可能有一些复杂,一般也很少这样使用。
vector<int> v(3,100);
queue<int,vector<int> > s(v); //注意,> >符号之间需要有一个空格隔开
通过标准的方式创建向量数组,然后通过复制构造函数的方式进行创建,其内容就是vector数组的全部内容。
4. 迭代器
栈和队列都属于一种特殊的数据结构,只能通过访问顶层数据并不断剔除数据的方法进行全部访问,因此没有直接的迭代器。
5. 常用接口
我们预先通过queue<int> q创建了一个队列,命名为q,方便举例。
a)大小size()
返回链表元素的个数
函数原型:size_type size() const;
b) 入队push()
进行入队操作,在队尾处进行插入
函数原型:void push (const value_type& val);
q.push(100);
c) 出队pop()
进行出队操作,在对头出进行弹出
函数原型:void pop();
q.pop();
d)访问队头元素front()
访问对头元素,可以返回其数值,也可以进行相应的操作,这里更加建议多使用front()访问队头数据,因为我们进行出队操作均是从队头进行出队的。
函数原型:
value_type& front();
const value_type& front() const;
q.front()+=500; //对队头元素进行修改
e) 访问队尾元素back()
访问队尾元素,较为少用。
函数原型:
value_type& back();
const value_type& back() const;
q.back()+=500; //对队尾元素进行修改
f) 判空empty()
返回一个bool类型的值,只存在真和假,当队列为空时为真,不为空时为假