第 8 章 初识 STL(上)

8.2 序列式容器

8.2.1 序列式容器概述

1.vector(向量)的实现及访问特点

动态分配的数组。向量容器中插入新元素时,插入位置之后的元素都要被顺序地向后移动,效率不高。在删除向量容器中的元素时,被删除元素后面的元素会顺序向前移动,将被删除元素留下的空位补上,而后面闲置的空间并不会被释放,效率不高。

2.deque(双端队列)的实现及访问特点

向两端加入新元素时,如果这一段的分段数组未满,则可以直接加入,如果这一段的分段数组已满,只需创建新的分段数组,并把数组的首地址存储到deque容器中即可,效率较高。当删除双端队列两端的元素时,由于不需要发生元素的移动,效率非常高。向双端队列的中间插入元素时,插入位置越靠中间,效率越低。当删除双端队列中间的元素时,元素越靠中间,效率越低。

3.list(列表)的实现及访问特点

插入删除效率都很高。

8.2.2 vector 类模板

1.创建vector对象

(1)指定容器大小

vector<元素类型>对象名(容器大小);

(2)指定初始值

vector<元素类型>对象名(容器大小, 元素初始值);

(3)列表初始化

vector<int> v1{1, 2};                //v1有两个元素1、2
vector<string> v2{ "a", "b", "c" };  //v2有三个元素,分别是字符串"a","b","c"

(4)初始化状态为空

(5)初始化另一个vector容器

vector<int> v1(10, 1);
vector<int> v2(v1);
vector<int> v3 = v2;

2.获取容器容量和大小

v.capacity();
v.size();

3.赋值函数

v.assign(n, elem);        //将n个elem元素赋值给容器
v.assign(begin, end);     //将[begin, end)左闭右开区间中的元素赋值给容器

4.访问容器中的元素

v.at(int idx);

该函数返回索引指向的数据。

5.从尾部插入和删除元素

v.push_back(type elem& t);
v.pop_back();

元素的值都一样,需要指定大小。元素的值有所不同,不需要指定容量大小。

6.获取头部和尾部元素

v.front();
v.back();

返回元素的引用。

v.begin();
v.end();

返回头尾元素的迭代器(指针)。

7.插入和删除元素

v.insert(pos, elem);        //在pos位置上插入元素elem,返回新数据的位置
v.insert(pos, n, elem);     //在pos位置上插入n个elem元素,无返回值
v.insert(pos, begin, end);  //在pos位置插入[begin, end)区间的数据,无返回值

erse()函数用于移除容器中的元素。

v.erase(pos);            //移除pos位置上的元素,返回下一个数据的位置
v.erase(begin, end);     //移除[begin, end)区间的数据,返回下一个元素的位置

insert()函数与erase()函数中的位置只能由begin()或end()返回的迭代器来指示,而不能用纯粹的数字作为参数。

8.2.3 deque 类模板

1.创建deque对象

deque<T> d;                //创建一个空的deque对象
deque<T> d(n);             //创建一个有n个数据的对象,默认值由构造函数提供
deque<T> d(n,elem);        //创建一个有n个数据的对象,元素值都是elem
deque<T> d(begin, end);    //创建一个以[begin, end)区间为元素的对象
deque<T> d(d1);            //创建d对象,用d1对象初始化

2.赋值

d.assign(n, elem);
d.assign(begin, end);

3.元素访问

d[int idx];        //重载了[]运算符,可以用[]直接访问
d.at(int idx);     //访问idx位置上的元素
d.front();         //返回第一个元素
d.back();          //返回最后一个元素
d.begin();         //返回第一个元素的迭代器
d.end();           //返回指向最后一个元素的下一个迭代器

4.添加元素

d.push_back();                //在尾部插入元素
d.push_front();               //在头部插入元素
d.insert(pos, elem);          //在pos位置上插入元素elem
d.insert(pos, n, elem);       //在pos位置上插入n个元素elem
d.insert(pos, begin, end);    //在pos位置上插入[begin, end)区间的值作为元素

5.删除元素

d.pop_back();            //从尾部删除元素
d.pop_front();           //从头部删除元素
d.erase(pos);            //从中间删除元素
d.erase(begin, end);     //删除[begin, end)区间的元素

8.2.4 list 类模板

双向链表。

1.创建对象

list<T> lt;                //创建一个空的列表对象
list<T> lt(n);             //创建一个列表对象,大小为
list<T> lt(n, elem);       //创建一个列表对象,包含n个elem元素
list<T> lt(begin, end);    //创建一个列表对象,用[begin, end)区间的值为元素赋值
list<T> lt(lt);            //创建一个列表对象,用另一个对象初始化

2.赋值

lt.assign(n, elem);        //将n个elem元素的值赋值给lt
lt.assign(begin, end);     //用[begin, end)区间的值给lt中的元素赋值

3.元素访问

lt.front();        //返回第一个元素
lt.back();         //返回最后一个元素
lt.begin();        //返回第一个元素的迭代器
lt.end();          //返回指向最后一个元素的下一个迭代器

4.添加元素

lt.push_back();                //在尾部插入元素
lt.push_front();               //在头部插入元素
lt.insert(pos, elem);          //在pos位置上插入元素elem
lt.insert(pos, n, elem);       //在pos位置上插入n个元素elem
lt,insert(pos, begin, end);    //在pos位置上插入[begin, end)区间的值作为元素

5.删除元素

lt.pop_back();            //从尾部删除元素
lt.pop_front();           //从头部删除元素
lt.erase(pos);            //从中间删除元素
lt.erase(begin, end);     //删除[begin, end)区间的元素
lt.erase(elem);           //从容器中删除所有与elem匹配的元素

6.merge()函数与sort()函数

合并两个list

lt.merge(list& lt1);    //合并后,容器中的元素按从小到大排列
lt.sort();

7.splice()函数

用于连接两个list对象

lt.splice(iterator it, list& lt1);    //将容器lt1插入到迭代器lt知识的位置前
lt.splice(iterator it, list& lt1, iterator first);    //将lt1中的元素first插入到迭代器it知识的位置前
lt.splice(iterator it, list& lt1, iterator first, iterator last);    //将lt1容器中[first, last)区间的元素插入到迭代器it指示的位置前面

一旦合并完成,lt1中的元素就是被“移走”。

8.3 关联型容器

8.3.1 关联型容器概述

内部实现为一个二叉树

1.set(集合)与multiset(多重集合)

set用来存储无重复的元素,multiset用来存储有重复的元素。集合中的元素值不可以被直接修改,因为这些元素时自动排序的,如果想要修改某一个元素值,必须先删除原有的元素,在插入新的元素。

2.map(映射)与multimap(多重映射)

映射与几何的主要区别在于,集合的元素本身是键本身,而映射的元素类型是由键和附加数据所构成的二元组。在映射的二叉树结点中需要存储两种类型的数据,用来定位的数据称为键,另一个存储元素值的数据称为值,通常也说映射中存储的是一对键值对。

map与multimap的主要区别是map存储的是无重复键值的元素对,而multimap允许相同的键值重复出现。

8.3.2 set/multiset类模板

1.创建对象

set<T> s;                    //创建一个空的set集合,默认升序排列
set<T, op> s;                //创建一个空的set集合,按op顺序排列
set<T> s(begin, end);        //创建一个集合,用[begin, end]区间为其初始化
set<T, op> s(begin, end);    //创建集合,用[begin, end]区间为其初始化并按op规则排序
set<T> s(s1);                //创建一个空的set集合,用另一个集合s1初始化

greater<T>从大到小排序,less<T>从小到大排序,包含在头文件functional中。默认为升序。

2.集合的大小、元素的查找和统计

s.size();        //返回容器中元素的数目
s.max_size();    //返回容器中可容纳的最大元素数量
s.empty();       //判断容器是否为空
s.find(elem);    //返回key元素的位置,返回值是迭代器类型
s.count(elem);   //返回元素elem的个数

3.获取头尾部元素

s.begin();    //返回容器中首元素的迭代器
s.end();      //返回容器中最后一个元素之后的迭代器

4.插入和删除元素

s.insert(elem);            //在容器中插入元素elem
s.insert(pos, elem);       //在pos位置插入元素elem
s.insert(begin, end);      //在容器中插入[begin, end)区间的值

对set容器来说,insert()返回值类型是pair<iterator, bool>对象,第一个参数是迭代器,指示元素插入的位置,第二个参数代表元素是否插入成功。multiset的返回值类型是pair<iterator>。

s.erase(pos);            //删除pos位置上的元素
s.erase(begin, end);     //删除[begin, end)区间上的元素
s.erase(elem);           //删除元素elem
#include <iostream>
#include <set>
#include <functional>
using namespace std;
int main()
{
	set<int, greater<int> > s;	//创建一个set容器,元素按降序排列
	multiset<char> ms;			//创建一个multiset容器
	cout << "s能容纳的最大元素数量" << s.max_size() << endl;
	cout << "ms能容纳的最大元素数量" << ms.max_size() << endl;
	//向s中插入元素
	pair<set<int>::iterator, bool> ps;
	ps = s.insert(12);
	if (ps.second == true)
		cout << "insert success" << endl;
	s.insert(39);
	s.insert(32);
	s.insert(26);
	//向ms中插入元素
	ms.insert('a');
	ms.insert('z');
	ms.insert('T');
	ms.insert('u');
	ms.insert('u');
	//输出两个容器中的元素
	set<int>::iterator its;		//创建s容器的迭代器,用于获取元素
	cout << "s容器中元素:";
	for (its = s.begin(); its != s.end(); its++)
		cout << *its << " ";
	cout << endl;
	multiset<char>::iterator itms;	//创建ms容器的迭代器
	cout << "ms容器中的元素:";
	for (itms = ms.begin(); itms != ms.end(); itms++)
		cout << *itms << " ";
	cout << endl;
	
	//查找两个容器中头尾元素
	cout << "s头元素" << *s.begin()	<< endl;
	cout << "ms尾元素" << *(--ms.end()) << endl;
	//查找ms容器中u元素出现的次数
	cout << "ms容器中u元素出现的次数:" << ms.count('u') << endl;
	system("pause");
	return 0;		 
 } 

pair类模板

其主要作用是将两个数据组成一个数据,用来表示一个二元组或一个元素对,两个数据可以是同一个类型也可以是不同的类型。

1.创建pair对象

pair<int, float> p1;    //调用构造函数来创建pair对象
make_pair(1,1.2);      //调用make_pair()函数来创建pair对象

2.pair对象的使用

pair<int, float> p1(1, 1.2);
cout << p1.first << endl;
cout << p1.second << endl;

8.3.3 map/multimap类模板

map与multimap中存储的是元素对(key-value)。

1.创建对象

map<T1, T2> m;                    //创建一个空的map容器
map<T1, T2, op> m;                 //创建一个空的map容器,以op为准则排序
map<T1, T2> m(begin, end);        //创建一个map容器,容[begin, end)区间赋值
map<T1, T2> m(begin,end,op);      //创建map,用[begin, end)区间值赋值op为排序准则
map<T1, T2> m(m1);                //创建一个map容器,用另一个map容器m1初始化

2.元素的访问与统计及容器大小

m.at(key);
m[key];

map容器可以通过这两种方式来随机访问容器中元素,但multimap中允许存在重复的键值,因此无法使用这两种方式来随机访问容器中的元素。

m.cout(key);
m.max_size();
m.size();

3.获取头尾部元素

m.begin();    //返回容器中首元素的迭代器
m.end();      //返回容器中最后一个元素之后的迭代器

4.插入和删除元素

m.insert(elem);        //在容器中插入元素elem
m.insert(pos, elem);   //在pos位置插入元素elem
m.insert(begin, end);  //在容器中插入[begin, end)区间的值

(1)使用pair<>构建键值对对象

map<int, float> m;                    //也适用于multimap容器
m.insert(pair<int, float>(10,2.3));

(2)使用make_pair()函数构建键值对对象

map<int, float> m;            //也适用于multimap对象
m.insert(make_pair(10,2.3));  

(3)使用value_type标志

value_type是容器中自定义的数据类型,它可以将传入的10与2.3自动与容器类型匹配,将数据插入到容器中。insert()与value_type结合使用也可以将数据插入到容器中

map<int, float> m;                //也适用于multimap容器
m.insert(map<int,float>::value_type(10,2.3));

使用下标方式插入元素

m[key] = value;    //m只能是map容器,不适用于multimap

m[key]返回一个引用,该引用指向键值为key的元素,如果该元素尚未存在,则插入该元素,如果该元素已经存在,则新插入的元素会覆盖原有的元素。

m[key]中的索引值“key”不一定是整型,它与容器中的键类型对应

map<char,string> m;
m1['a'] = "abc";

下标法只适用于map容器

map与multimap也提供了erase()函数用于删除容器中的元素

m.erase(pos);            //删除位置pos上的元素,返回下一个元素的迭代器
m.erase(begin, end);     //删除[begin, end)范围内的元素,返回下一个元素的迭代器
m.erase(key);            //删除键为key的元素对
#include <iostream>
#include <map>
#include <functional>
#include <string>
using namespace std;
//定义printm()函数输出map容器元素
void printm(map<char, double> mymap)
{
	pair<char, double> p;	//创建pair对象,map中元素是成对的,也要成对输出
	map<char, double>::iterator it;
	for (it = mymap.begin(); it != mymap.end(); it++)
	{
		p = (pair<char, double>)*it;	//将迭代器指向的一对元素存放到p中
		cout << p.first << "->" << p.second << endl; //输出一对元素 
	 } 
 } 
//定义printmt()函数输出multimap容器
void printmt(multimap<int, string> mymul)
{
	pair<int, string> p;
	multimap<int, string>::iterator it;
	for (it = mymul.begin(); it != mymul.end(); it++)
	{
		p = (pair<int, string>)*it;
		cout << p.first << "->" << p.second << endl; 
	}
 } 
 
 
int main()
{
	map<char, double> m;		//创建一个map容器 
	//向容器m中插入元素
	m['a'] = 1.2;
	m['b'] = 3.6;
	m['c'] = 6.4; 
	m['d'] = 0.8; 
	m['e'] = 5.3; 
	m['f'] = 3.6; 
	
	cout << "map:" << endl;
	printm(m);
	cout << "map中key = a的值:" << m.at('a') << endl;
	cout << "map中key = f的元素出现次数:" << m.count('f') << endl;
	
	multimap<int, string> mt;	//创建一个multimap容器
	//向容器mt中插入元素
	mt.insert(pair<int, string>(1, "chuan"));
	mt.insert(make_pair(1, "zhi"));
	mt.insert(multimap<int, string>::value_type(3, "bo"));
	mt.insert(multimap<int, string>::value_type(4, "ke"));
	
	cout << endl << "multimap:" << endl;
	printmt(mt);
	cout << "multimap头部元素:";
	pair<int, string> p;
	p = (pair<int, string>)*mt.begin();
	cout << p.first << "->" << p.second << endl;
	cout << "multimap尾部元素:";
	p = (pair<int, string>)*(--mt.end());    //end()函数返回的是末尾元素的下一个位置
	cout << p.first << "->" << p.second << endl;
	system("pause");
	return 0; 
}

8.4 迭代器

迭代器(iterator)是连接容器与算法的纽带,它起源于指针又高于指针,并发展成为分隔容器与算法的分界。从容器的角度看,只需提供适当地迭代器,就可以遍历容器中的数据,而不必关心容器中数据将用于何种操作;而从算法的角度看,只需要通过迭代器操作数据,不必关心容器为何物。迭代器两侧的程序员都可以专注于构造自己的代码而无须为对面的“世界”分散精力,因此很好地实现了数据结构与算法的分离。

8.4.1 迭代器概述

泛化的指针。迭代器本身是一个对象,这个对象可以遍历STL容器内部全部的对象,它能够反复地对STL容器内容进行访问。迭代器所提供的基本操作如下所示:

  • 获取当前被指向的元素,用“*”或“->”表示。
  • 指向下一个元素,迭代器的增量使用运算符“++”。
  • 相等,使用运算符“==”。
  • 除了输出迭代器外,其他迭代器可以获得任意两个迭代器之间的位置(使用distance函数)。
  • 随机存取迭代器可以通过加减整数,取得相对地址(其他迭代器不可以)。

STL提供了五种基本的迭代器:输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机迭代器。这五种迭代器的关系如图所示:

8.4.2 输入迭代器与输出迭代器

1.输入迭代器

只能一次一个地向前读取元素,并按此顺序传回元素值,是不可重复的单向遍历。输入迭代器支持的操作如下所示:

  • *:用于从迭代器中读取实际元素。
  • ->:读取实际元素的成员。
  • ++:无论是放在迭代的前面还是后面,都代表向前移动一个单位。
  • ==:判断两个迭代器是否相等。
  • !=:判断两个迭代器是否不相等。
  • 迭代器的复制操作,产生一个迭代器副本。

2.输出迭代器

与输入迭代器相反,其作用是将元素值逐个写入,即只能逐个元素赋值。输出迭代器也支持对序列进行单向遍历,当把迭代器移到下一个位置后,也不能保证之前的迭代器是有效的。输出迭代器可以实现*、->、++和复制操作。

8.4.3 前向迭代器

具有输入迭代器和输出迭代器的全部功能。前向迭代器支持对序列进行可重复的单向遍历,可以多次解析一个迭代器指定的位置,因此可以对一个值进行多次读写。

前向迭代器不再有输出迭代器关于“++”运算符的自增操作,对元素的写入操作必须交替进行限制。

8.4.4 双向迭代器与随机访问迭代器

双向迭代器是在单项迭代器的基础上增加了一个反向操作,就是它既可以前进,又可以后退,因此它比单向迭代器新增一个功能,进行自减操作,例如it--或者--it。

随机访问迭代器在双向迭代器的基础上,有支持直接将迭代器向后或向前移动n个元素,而且还支持比较运算的操作,因此随机访问迭代器的功能几乎和指针一样,可以进行如下操作

it1 += n;        //将迭代器it1向下移动n个元素
it1 -= n;        //将迭代器it1向上移动n个元素
it1 + n;         //获得指向迭代器it1向下第n个元素的迭代器
it1 - n;         //获得指向迭代器it1向上第n个元素的迭代器
it1 - it2;       //求两个迭代器之间有有多少个元素
it1 op it2;      //op指比较运算符,如<,<=,>,>=,用于比较两个迭代器的位置先后关系
it1[n];          //等价于*(it1+n);

8.5 迭代器配适器

逆向迭代器、插入迭代器和流迭代器

8.5.1 逆向迭代器

它能够重新定义迭代器的递增运算和递减运算,使其行为正好相反,逆向迭代器的遍历顺序如下

STL中的容器提供了begin()与end()函数返回一个随机访问迭代器来访问元素,除此之外,各容器还提供了rbegin()与read()这样一对函数,用于返回一个逆向迭代器。rbegin()返回的是逆向遍历的第一个元素,即倒数第一个元素,rend()返回的是逆向遍历的末尾元素的最后一个位置,即第一个元素的上一个位置,其实这个位置已经不在容器中了。

逆向迭代器与正常迭代器的相互转换,reverse_iterator()

将逆向迭代器转换为正常迭代器,itr.base()

8.5.2 插入迭代器

通过插入迭代器赋值时,迭代器将会插入一个新元素,而不会将原有的元素覆盖。

  • back_insert_iterator<C>:在容器末端插入数据,其内部调用的是push_back()函数,因此,只有在提供push_back()函数的容器中才能使用,例如vector、deque、list。
  • front_insert_iterator<C>:在容器前端插入数据,其内部调用的是push_front()函数,将元素插入容器最前端,因此只有在提供push_front()函数的容器中才能使用,例如deque、list,而vector容器就不再适用。向前插入器在插入多个元素时,是以逆序的方式插入的,因为他总是将后一个元素插入到前一个元素的前面。
  • insert_iterator<C>:在容器的指定位置插入数据,其实参所表示的位置前插入元素,其内部调用的是insert()函数,所有的容器都提供了insert()成员函数,因此它适用于所有的容器,也是唯一可用于关联容器上的插入迭代器,但对关联容器来说,待插入位置只是一个提示,元素的真正位置需要根据其实质或键值而定,如果提示位置不当,则可能会更糟糕。
#include <iostream>
#include <deque>
#include <algorithm>
#include <iterator>
using namespace std;

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6 };
	deque<int> d1;							//创建一个空的deque<int>对象
	cout << "d1(1):";			
	copy(arr, arr + 6, back_inserter(d1));	//将数组arr中的元素插入到d1容器中 
	//将d1容器中的元素用输出流迭代器输出到屏幕
	copy(d1.begin(), d1.end(), ostream_iterator<int>(cout, " "));
	cout << endl;
	
	cout << "d1(2):";
	front_inserter(d1) = 11;				//前端插入元素 
	front_inserter(d1) = 22; 
	copy(d1.begin(), d1.end(), ostream_iterator<int>(cout, " "));
	cout << endl;
	
	cout << "d1(3):";
	//将d1容器中的所有元素再次插入到d1容器的前端
	copy(d1.begin(), d1.end(), front_inserter(d1));
	copy(d1.begin(), d1.end(), ostream_iterator<int>(cout, " "));
	cout << endl;
	
	cout << "d1(4):";
	inserter(d1, d1.end()) = 33;			//将33插入在d1容器的尾部
	inserter(d1, d1.end()) = 44;			
	copy(d1.begin(), d1.end(), ostream_iterator<int>(cout, " "));
	cout << endl;
	
	deque<int> d2;							//创建一个空的deque容器d2
	cout << "d2:";
	//将d1容器的元素插入到d2容器的头部
	copy(d1.begin(), d1.end(), inserter(d2, d2.begin()));
	copy(d2.begin(), d2.end(), ostream_iterator<int>(cout, " "));
	cout << endl;
	system("pause");
	return 0;
 } 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值