1哪些是STL?

STL(StandardTemplateLibrary),即标准模板库,是一个具有工业硬度的,高效的C++程序库。它被容纳于C++标准程序库(C++StandardLibrary)中,是ANSI/ISOC++标准中最新的也是极具革命性的一部份。该库包含了众多在计算机科学领域里所常用的基本数据结构和基本算法。为广大C++程序员们提供了一个可扩充的应用框架,高度彰显了软件的可复用性。

STL的一个重要特征是数据结构和算法的分离。虽然这是个简单的概念,但这些分离确实促使STL显得十分通用。诸如,因为STL的sort()函数是完全通用的,你可以用它来操作几乎任何数据集合,包括数组,容器和字段;

STL另一个重要特点是它不是面向对象的。为了具有足够通用性,STL主要依赖于模板而不是封装,承继和虚函数(多态性)——OOP的三个要素。你在STL中找不到任何显著的类承继关系。这似乎是一种倒退,但这恰好是促使STL的组件具有广泛通用性的底层特点。另外,因为STL是基于模板,内联函数的使用促使生成的代码短小高效;

从逻辑层次来看,在STL中彰显了子类化程序设计的思想,引入了众多新的名词,例如像需求(requirements),概念(concept),模型(model),容器(container),算法(algorithmn),迭代子(iterator)等。与OOP(object-orientedprogramming)中的多态(polymorphism)一样,子类也是一种软件的复用技术;

从实现层次看,整个STL是以一种类型参数化的方法实现的,这些方法基于一个在先前C++标准中没有出现的语言特点--模板(template)。

2STL内容介绍

STL中六大组件:

容器(Container),是一种数据结构,如list,vector,和deques,以模板类的方式提供。为了访问容器中的数据,可以使用由容器类输出的迭代器;

迭代器(Iterator),提供了访问容器中对象的方式。比如,可以使用一对迭代器指定list或vector中的一定范围的对象。迭代器就好似一个表针。事实上,C++的表针也是一种迭代器。并且,迭代器也可以是这些定义了operator*()以及其他类似于表针的操作符地方式的类对象;

算法(Algorithm),是拿来操作容器中的数据的模板函数。诸如,STL用sort()来对一个vector中的数据进行排序,用find()来搜索一个list中的对象,函数本身与她们操作的数据的结构和类型无关,因而她们可以在从简单链表到高度复杂容器的任何数据结构上使用;

仿函数(Functor)

适配器(Adaptor)

分配器(allocator)

2.1容器

STL中的容器有队列容器和关联容器,容器适配器(congtaineradapters:stack,queue,priorityqueue),位集(bit_set),串包(string_package)等等。

(1)序列式容器(Sequencecontainers),每位元素都有固定位置--取决于插入时机和地点,和元素值无关,vector、deque、list;

Vector:将元素放在一个动态字段中加以管理,可以随机存取元素(用索引直接存取),链表尾部添加或移除元素十分快速。并且在中部或腹部安插元素比较费时;

Deque:是“double-endedqueue”的简写,可以随机存取元素(用索引直接存取),链表背部和尾部添加或移除元素都十分快速。并且在中部或背部安插元素比较费时;

List:单向数组,不提供随机存取(按次序走到需存取的元素,O(n)),在任何位置上执行插入或删掉动作都十分迅速,内部只需调整一下表针;

(2)关联式容器(Associatedcontainers),元素位置取决于特定的排序准则,和插入次序无关,set、multiset、map、multimap等。

Set/Multiset:内部的元素根据其值手动排序,Set内的相同数值的元素只能出现一次,Multisets内可包含多个数值相同的元素,内部由二叉树实现,以便查找;

Map/Multimap:Map的元素是成对的通配符/实值,内部的元素根据其值手动排序,Map内的相同数值的元素只能出现一次,Multimaps内可包含多个数值相同的元素,内部由二叉树实现,以便查找;

容器类手动申请和释放显存,无需new和delete操作。

2.2STL迭代器

Iterator(迭代器)模式又称Cursor(游标)模式,用于提供一种方式次序访问一个聚合对象中各个元素,而又不需曝露该对象的内部表示。或则这样说可能更容易理解:Iterator模式是运用于聚合对象的一种模式,通过运用该模式,促使我们可以在不晓得对象内部表示的情况下,依照一定次序(由iterator提供的方式)访问聚合对象中的各个元素。

迭代器的作用:才能让迭代器与算法不干扰的互相发展,最后又能无间隙的黏合上去,重载了*,++,==,!=,=运算符。用以操作复杂的数据结构,容器提供迭代器,算法使用迭代器;常见的一些迭代器类型:iterator、const_iterator、reverse_iterator和const_reverse_iterator.

相关视频推荐

黑红树,在Linux内核的这些故事

【C++前端开发】c++11,80行代码实现高效灵活的定时器

学习地址:C/C++Linux鏈嶅姟鍣ㄥ紑鍙�/鍚庡彴鏋舵瀯甯堛€愰浂澹版暀鑲层€�-瀛︿範瑙嗛鏁欑-鑵捐璇惧爞

须要C/C++Linux服务器构架师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8Slinux软件,Docker,TCP/IP,解释器,DPDK,ffmpeg等),免费分享

C++ STL 容器和迭代器_linux c++定时器_STL C++ 数据结构和算法

2.3算法

函数库对数据类型的选择对其可重用性起着至关重要的作用。举例来说,一个求方根的函数,在使用浮点数作为其参数类型的情况下的可重用性肯定比使用整型作为它的参数类性要高。而C++通过模板的机制准许延后对个别类型的选择,直至真正想使用模板或则说对模板进行特化的时侯,STL就借助了这一点提供了相当多的有用算法。它是在一个有效的框架中完成这种算法的——你可以将所有的类型界定为少数的几类,之后就可以在模版的参数中使用一种类型替换掉同一种类中的其他类型。

STL提供了大概100个实现算法的模版函数,例如算法for_each将为指定序列中的每一个元素调用指定的函数,stable_sort以你所指定的规则对序列进行稳定性排序等等。只要我们熟悉了STL以后,许多代码可以被大大的通分,只须要通过调用一两个算法模板,就可以完成所须要的功能并大大地提高效率。

算法部份主要由头文件,和组成。

是所有STL头文件中最大的一个(虽然它挺好理解),它是由一大堆模版函数组成的,可以觉得每位函数在很大程度上都是独立的,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等等。

容积很小,只包括几个在序列里面进行简单物理运算的模板函数,包括乘法和除法在序列上的一些操作。

中则定义了一些模板类,用以申明函数对象。

STL中算法大致分为四类:

以下对所有算法进行细致分类并标注功能:

查找算法(13个):判定容器中是否包含某个值

adjacent_find:在iterator对标示元素范围内,查找一对相邻重复元素,找到则返回指向这对元素的第一个元素的ForwardIterator。否则返回last。重载版本使用输入的二元操作符取代相等的判别。

binary_search:在有序序列中查找value,找到返回true。重载的版本实用指定的比较函数对象或函数表针来判定相等。

count:借助等于操作符,把标志范围内的元素与输入值比较,返回相等元素个数。

count_if:借助输入的操作符,对标志范围内的元素进行操作,返回结果为true的个数。

equal_range:功能类似equal,返回一对iterator,第一个表示lower_bound,第二个表示upper_bound。

find:借助底层元素的等于操作符,对指定范围内的元素与输入值进行比较。当匹配时,结束搜索,返回该元素的一个InputIterator。

find_end:在指定范围内查找”由输入的另外一对iterator标志的第二个序列”的最后一次出现。找到则返回最后一对的第一个ForwardIterator,否则返回输入的”另外一对”的第一个ForwardIterator。重载版本使用用户输入的操作符代替等于操作。

find_first_of:在指定范围内查找”由输入的另外一对iterator标志的第二个序列”中任意一个元素的第一次出现。重载版本中使用了用户自定义操作符。

find_if:使用输入的函数替代等于操作符执行find。

lower_bound:返回一个ForwardIterator,指向在有序序列范围内的可以插入指定值而不破坏容器次序的第一个位置。重载函数使用自定义比较操作。

upper_bound:返回一个ForwardIterator,指向在有序序列范围内插入value而不破坏容器次序的最后一个位置,该位置标志一个小于value的值。重载函数使用自定义比较操作。

search:给出两个范围,返回一个ForwardIterator,查找成功指向第一个范围内第一次出现子序列(第二个范围)的位置,查找失败指向last1。重载版本使用自定义的比较操作。

search_n:在指定范围内查找val出现n次的子序列。重载版本使用自定义的比较操作。

排序和通用算法(14个):提供元素排序策略

inplace_merge:合并两个有序序列,结果序列覆盖两端范围。重载版本使用输入的操作进行排序。

merge:合并两个有序序列,储存到另一个序列。重载版本使用自定义的比较。

nth_element:将范围内的序列重新排序,使所有大于第n个元素的元素都出现在它上面,而小于它的都出现在前面。重载版本使用自定义的比较操作。

partial_sort:对序列做部份排序,被排序元素个数恰好可以被放在范围内。重载版本使用自定义的比较操作。

partial_sort_copy:与partial_sort类似,不过将经过排序的序列复制到另一个容器。

partition:对指定范围内元素重新排序,使用输入的函数,把结果为true的元素放到结果为false的元素之前。

random_shuffle:对指定范围内的元素随机调整顺序。重载版本输入一个随机数形成操作。

reverse:将指定范围内元素重新反序排序。

reverse_copy:与reverse类似,不过将结果写入另一个容器。

rotate:将指定范围内元素移到容器末尾,由middle指向的元素成为容器第一个元素。

rotate_copy:与rotate类似,不过将结果写入另一个容器。

sort:以倒序重新排列指定范围内的元素。重载版本使用自定义的比较操作。

stable_sort:与sort类似,不过保留相等元素之间的次序关系。

stable_partition:与partition类似,不过不保证保留容器中的相对次序。

删掉和替换算法(15个)

copy:复制序列

copy_backward:与copy相同,不过元素是以相反次序被拷贝。

iter_swap:交换两个ForwardIterator的值。

remove:删掉指定范围内所有等于指定元素的元素。注意,该函数不是真正删掉函数。外置函数不适宜使用remove和remove_if函数。

remove_copy:将所有不匹配元素复制到一个制订容器,返回OutputIterator指向被拷贝的末元素的下一个位置。

remove_if:删掉指定范围内输入操作结果为true的所有元素。

remove_copy_if:将所有不匹配元素拷贝到一个指定容器。

replace:将指定范围内所有等于vold的元素都用vnew取代。

replace_copy:与replace类似,不过将结果写入另一个容器。

replace_if:将指定范围内所有操作结果为true的元素用新值替代。

replace_copy_if:与replace_if,不过将结果写入另一个容器。

swap:交换储存在两个对象中的值。

swap_range:将指定范围内的元素与另一个序列元素值进行交换。

unique:消除序列中重复元素,和remove类似,它也不能真正删掉元素。重载版本使用自定义比较操作。

unique_copy:与unique类似,不过把结果输出到另一个容器。

排列组合算法(2个):提供估算给定集合按一定次序的所有可能排列组合

next_permutation:取出当前范围内的排列,并重新排序为下一个排列。重载版本使用自定义的比较操作。

prev_permutation:取出指定范围内的序列并将它重新排序为上一个序列。倘若不存在上一个序列则返回false。重载版本使用自定义的比较操作。

算术算法(4个)

accumulate:iterator对标示的序列段元素之和,加到一个由val指定的初始值上。重载版本不再做乘法,而是传进来的二元操作符被应用到元素上。

partial_sum:创建一个新序列,其中每位元素值代表指定范围内该位置前所有元素之和。重载版本使用自定义操作代替乘法。

inner_product:对两个序列做内积(对应元素相加,再求和)并将内积加到一个输入的初始值上。重载版本使用用户定义的操作。

adjacent_difference:创建一个新序列,新序列中每位新值代表当前元素与上一个元素的差。重载版本用指定二元操作估算相邻元素的差。

生成和异变算法(6个)

fill:将输入值赋给标志范围内的所有元素。

fill_n:将输入值赋给first到first+n范围内的所有元素。

for_each:用指定函数依次对指定范围内所有元素进行迭代访问,返回所指定的函数类型。该函数不得更改序列中的元素。

generate:连续调用输入的函数来填充指定的范围。

generate_n:与generate函数类似,填充从指定iterator开始的n个元素。

transform:将输入的操作作用与指定范围内的每位元素,并形成一个新的序列。重载版本将操作作用在一对元素上,另外一个元素来自输入的另外一个序列。结果输出到指定容器。

关系算法(8个)

equal:假如两个序列在标志范围内元素都相等,返回true。重载版本使用输入的操作符取代默认的等于操作符。

includes:判定第一个指定范围内的所有元素是否都被第二个范围包含,使用底层元素的实现了栈的功能,但其内部使用次序容器vector来储存数据。(相当于是vector表现出了栈的行为)。

容器适配器

要使用适配器,须要加入一下头文件:

#include//stack

#include//queue、priority_queue

C++ STL 容器和迭代器_linux c++定时器_STL C++ 数据结构和算法

1、初始化

stackstk(dep);

2、覆盖默认容器类型

stack>stk;

2.5.1stack

stack s;
stack< int, vector > stk;  //覆盖基础容器类型,使用vector实现stk
s.empty();  //判断stack是否为空,为空返回true,否则返回false
s.size();   //返回stack中元素的个数
s.pop();    //删除栈顶元素,但不返回其值
s.top();    //返回栈顶元素的值,但不删除此元素
s.push(item);   //在栈顶压入新元素item

实例:括弧匹配

#include
#include
#include
#include
using namespace std;
int main()
{
	string s;
	stack ss;
	while (cin >> s) 
	{
		bool flag = true;
		for (char c : s)  //C++11新标准,即遍历一次字符串s
		{
			if (c == '(' || c == '{' || c == '[')
			{
				ss.push(c);
				continue;
			}
			if (c == '}')
			{
				if (!ss.empty() && ss.top() == '{')
				{
					ss.pop();
					continue;
				}
				else
				{
					flag = false;
					break;
				}					
			}
			if (!ss.empty() && c == ']')
			{
				if (ss.top() == '[')
				{
					ss.pop();
					continue;
				}
				else
				{
					flag = false;
					break;
				}
			}
			if (!ss.empty() && c == ')')
			{
				if (ss.top() == '(')
				{
					ss.pop();
					continue;
				}
				else
				{
					flag = false;
					break;
				}
			}
		}
		if (flag)	cout << "Match!" << endl;
		else    cout << "Not Match!" << endl;
	}
}

2.5.2queue&priority_queue

queue q; //priority_queue q;
q.empty();  //判断队列是否为空
q.size();   //返回队列长度
q.push(item);   //对于queue,在队尾压入一个新元素
               //对于priority_queue,在基于优先级的适当位置插入新元素
 
//queue only:
q.front();  //返回队首元素的值,但不删除该元素
q.back();   //返回队尾元素的值,但不删除该元素
 
//priority_queue only:
q.top();    //返回具有最高优先级的元素值,但不删除该元素

3常用容器用法介绍3.1vector3.1.1基本函数实现

1.构造函数

2.降低函数

3.删掉函数

4.遍历函数

5.判定函数

6.大小函数

7.其他函数

8.看着清楚

1.push_back在链表的最后添加一个数据

2.pop_back除去链表的最后一个数据

.at-DomainParked得到编号位置的数据

4.begin得到链表头的表针

5.end得到链表的最后一个单元+1的表针

6.front得到链表头的引用

7.back得到链表的最后一个单元的引用

8.max_size得到vector最大可以是多大

9.capacity当前vector分配的大小

10.size当前使用数据的大小

11.resize改变当前使用数据的大小,假若它比当前使用的大,者填充默认值

12.reserve改变当前vecotr所分配空间的大小

13.erase删掉表针指向的数据项

14.clear清空当前的vector

15.rbegin将vector反转后的开始表针返回(虽然就是原先的end-1)

16.rend将vector反转构的结束表针返回(虽然就是原先的begin-1)

17.empty判定vector是否为空

18.swap与另一个vector交换数据

3.1.2基本用法

#include  
using namespace std;

3.1.3简单介绍

3.1.4实例

3.1.4.1pop_back()&push_back(elem)实例在容器最后移除和插入数据

#include 
#include 
#include 
using namespace std;
 
int main()
{
    vectorobj;//创建一个向量存储容器 int
    for(int i=0;i<10;i++) // push_back(elem)在数组最后添加数据 
    {
        obj.push_back(i);
        cout<<obj[i]<<",";    
    }
 
    for(int i=0;i<5;i++)//去掉数组最后一个数据 
    {
        obj.pop_back();
    }
 
    cout<<"n"<<endl;
 
    for(int i=0;i<obj.size();i++)//size()容器中实际数据个数 
    {
        cout<<obj[i]<<",";
    }
 
    return 0;
}

输出结果为:

0,1,2,3,4,5,6,7,8,9,
 
0,1,2,3,4,

3.1.4.2clear()消除容器中所有数据

#include 
#include 
#include 
using namespace std;
 
int main()
{
    vectorobj;
    for(int i=0;i<10;i++)//push_back(elem)在数组最后添加数据 
    {
        obj.push_back(i);
        cout<<obj[i]<<",";
    }
 
    obj.clear();//清除容器中所以数据
    for(int i=0;i<obj.size();i++)
    {
        cout<<obj[i]<<endl;
    }
 
    return 0;
}

输出结果为:

0,1,2,3,4,5,6,7,8,9,

3.1.4.3排序

#include 
#include 
#include 
#include 
using namespace std;
 
int main()
{
    vectorobj;
 
    obj.push_back(1);
    obj.push_back(3);
    obj.push_back(0);
 
    sort(obj.begin(),obj.end());//从小到大
 
    cout<<"从小到大:"<<endl;
    for(int i=0;i<obj.size();i++)
    {
        cout<<obj[i]<<",";  
    } 
 
    cout<<"n"<<endl;
 
    cout<<"从大到小:"<<endl;
    reverse(obj.begin(),obj.end());//从大到小 
    for(int i=0;i<obj.size();i++)
    {
        cout<<obj[i]<<",";
    }
    return 0;
}

输出结果为:

从小到大:
0,1,3,
 
从大到小:
3,1,0,

1.注意sort须要头文件#include

2.假如想sort来逆序,可重画sort

bool compare(int a,int b) 
{ 
    return ab,则为降序 
} 
int a[20]={2,4,1,23,5,76,0,43,24,65},i; 
for(i=0;i<20;i++) 
    cout<< a[i]<< endl; 
sort(a,a+20,compare);

3.1.4.4访问(直接链表访问&迭代器访问)

#include 
#include 
#include 
#include 
using namespace std;
 
int main()
{
    //顺序访问
    vectorobj;
    for(int i=0;i<10;i++)
    {
        obj.push_back(i);   
    } 
 
    cout<<"直接利用数组:"; 
    for(int i=0;i<10;i++)//方法一 
    {
        cout<<obj[i]<<" ";
    }
 
    cout<<endl; 
    cout<<"利用迭代器:" ;
    //方法二,使用迭代器将容器中数据输出 
    vector::iterator it;//声明一个迭代器,来访问vector容器,作用:遍历或者指向vector容器的元素 
    for(it=obj.begin();it!=obj.end();it++)
    {
        cout<<*it<<" ";
    }
    return 0;
}

输出结果为:

直接利用数组:0 1 2 3 4 5 6 7 8 9 
利用迭代器:0 1 2 3 4 5 6 7 8 9

3.1.4.5二维链表两种定义方式(结果一样)

方式一

#include 
#include 
#include 
#include 
using namespace std;
 
 
int main()
{
    int N=5, M=6; 
    vector<vector > obj(N); //定义二维动态数组大小5行 
    for(int i =0; i< obj.size(); i++)//动态二维数组为5行6列,值全为0 
    { 
        obj[i].resize(M); 
    } 
 
    for(int i=0; i< obj.size(); i++)//输出二维动态数组 
    {
        for(int j=0;j<obj[i].size();j++)
        {
            cout<<obj[i][j]<<" ";
        }
        cout<<"n";
    }
    return 0;
}

方式二

#include 
#include 
#include 
#include 
using namespace std;
 
 
int main()
{
    int N=5, M=6; 
    vector<vector > obj(N, vector(M)); //定义二维动态数组5行6列 
 
    for(int i=0; i< obj.size(); i++)//输出二维动态数组 
    {
        for(int j=0;j<obj[i].size();j++)
        {
            cout<<obj[i][j]<<" ";
        }
        cout<<"n";
    }
    return 0;
}

输出结果为:

0 0 0 0 0 0 
0 0 0 0 0 0 
0 0 0 0 0 0 
0 0 0 0 0 0 
0 0 0 0 0 0 

3.2deque

所谓的deque是”doubleendedqueue”的简写,双端队列不论在尾部或腹部插入元素,都非常迅速。而在中间插入元素则会比较费时,由于必须联通中间其他的元素。双端队列是一种随机访问的数据类型,提供了在序列两端快速插入和删掉操作的功能,它可以在须要的时侯改变自身大小,完成了标准的C++数据结构支队列的所有功能。

Vector是双向开口的连续线性空间,deque则是一种单向开口的连续线性空间。deque对象在队列的两端放置元素和删掉元素是高效的,而向量vector只是在插入序列的末尾时操作才是高效的。deque和vector的最大差别,一在于deque准许于常数时间内对头端进行元素的插入或移除操作,二在于deque没有所谓的capacity观念,由于它是动态地以分段连续空间组合而成,随时可以降低一段新的空间并链接上去。换句话说,像vector那样“因旧空间不足而重新配置一块更大空间,之后复制元素,再释放旧空间”这样的事情在deque中是不会发生的。也为此,deque没有必要提供所谓的空间预留(reserved)功能。

尽管deque也提供RandomAccessIterator,但它的迭代器并不是普通表针,其复杂度和vector不可同日而语,这其实涉及到各个运算层面。为此,除非必要,我们应尽可能选择使用vector而非deque。对deque进行的排序操作,为了最高效率,可将deque先完整复制到一个vector脸上,将vector排序后(借助STL的sort算法),再复制回deque。

deque是一种优化了的对序列两端元素进行添加和删掉操作的基本序列容器。一般由一些独立的区块组成,第一区块朝某方向扩充,最后一个区块朝另一方向扩充。它容许较为快速地随机访问但它不像vector一样把所有对象保存在一个连续的显存块,而是多个连续的显存块。而且在一个映射结构中保存对那些块以及次序的跟踪。

3.2.1申明deque容器

#include  // 头文件
deque deq;  // 声明一个元素类型为type的双端队列que
deque deq(size);  // 声明一个类型为type、含有size个默认值初始化元素的的双端队列que
deque deq(size, value);  // 声明一个元素类型为type、含有size个value元素的双端队列que
deque deq(mydeque);  // deq是mydeque的一个副本
deque deq(first, last);  // 使用迭代器first、last范围内的元素初始化deq

3.2.2deque的常用成员函数

deque deq;

deq:拿来访问单向队列中单个的元素。deq.front():返回第一个元素的引用。deq.back():返回最后一个元素的引用。deq.push_front(x):把元素x插入到单向队列的脸部。deq.pop_front():弹出单向队列的第一个元素。deq.push_back(x):把元素x插入到单向队列的尾部。deq.pop_back():弹出单向队列的最后一个元素。

3.2.3deque的一些特征

支持随机访问,即支持以及at(),而且性能没有vector好。可以在内部进行插入和删掉操作,但性能不及list。deque两端都还能快速插入和删掉元素,而vector只能在尾端进行。deque的元素存取和迭代器操作会稍稍慢一些,由于deque的内部结构会多一个间接过程。deque迭代器是特殊的智能表针,而不是通常表针,它须要在不同的区块之间跳转。deque可以包含更多的元素,其max_size可能更大,由于不止使用一块显存。deque不支持对容量和显存分配时机的控制。在不仅首尾两端的其他地方插入和删掉元素,都将会引起指向deque元素的任何pointers、references、iterators失效。不过,deque的显存重分配优于vector,由于其内部结构显示不须要复制所有元素。deque的显存区块不再被使用时,会被释放,deque的显存大小是可削减的。不过,是不是那么做以及如何做由实际操作版本定义。deque不提供容量操作:capacity()和reverse(),然而vector可以。

3.2.4实例

#include
#include
#include
using namespace std;
int main(void)
{
	int i;
	int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
	deque q;
	for (i = 0; i <= 9; i++)
	{
		if (i % 2 == 0)
			q.push_front(a[i]);
		else
			q.push_back(a[i]);
	}                                  /*此时队列里的内容是: {8,6,4,2,0,1,3,5,7,9}*/
	q.pop_front();
	printf("%dn", q.front());    /*清除第一个元素后输出第一个(6)*/
	q.pop_back();
	printf("%dn", q.back());     /*清除最后一个元素后输出最后一个(7)*/
	deque::iterator it;
	for (it = q.begin(); it != q.end(); it++) {
		cout << *it << 't';
	}
	cout << endl;
	system("pause");
	return 0;
}

输出结果:

3.3list3.3.1list定义

List是stl实现的单向数组,与向量(vectors)相比,它容许快速的插入和删掉,并且随机访问却比较慢。使用时须要添加头文件

#include

3.3.2list定义和初始化

listlst1;//创建空list

listlst2(5);//创建富含5个元素的list

listlst3(3,2);//创建富含3个元素的list

listlst4(lst2);//使用lst2初始化lst4

listlst5(lst2.begin(),lst2.end());//同lst4

3.3.3list常用操作函数

Lst1.assign()给list形参

Lst1.back()返回最后一个元素

Lst1.begin()返回指向第一个元素的迭代器

Lst1.clear()删掉所有元素

Lst1.empty()假如list是空的则返回true

Lst1.end()返回末尾的迭代器

Lst1.erase()删掉一个元素

Lst1.front()返回第一个元素

Lst1.get_allocator()返回list的配置器

Lst1.insert()插入一个元素到list中

Lst1.max_size()返回list能容纳的最大元素数目

Lst1.merge()合并两个list

Lst1.pop_back()删掉最后一个元素

Lst1.pop_front()删掉第一个元素

Lst1.push_back()在list的末尾添加一个元素

Lst1.push_front()在list的腹部添加一个元素

Lst1.rbegin()返回指向第一个元素的逆向迭代器

Lst1.remove()从list删掉元素

Lst1.remove_if()按指定条件删掉元素

Lst1.rend()指向list末尾的逆向迭代器

Lst1.resize()改变list的大小

Lst1.reverse()把list的元素倒转

Lst1.size()返回list中的元素个数

Lst1.sort()给list排序

Lst1.splice()合并两个list

Lst1.swap()交换两个list

Lst1.unique()删掉list中相邻重复的元素

3.3.4List使用实例

3.3.4.1迭代器遍历list

  for(list::const_iteratoriter = lst1.begin();iter != lst1.end();iter++)
    {
      cout<<*iter;
    }
    cout<<endl;

3.3.4.2综合实例1

#include 
#include 
#include 
#include 
using namespace std;
 
typedef list LISTINT;
typedef list LISTCHAR;
 
void main()
{
	//用LISTINT创建一个list对象
	LISTINT listOne;
	//声明i为迭代器
	LISTINT::iterator i;
 
	listOne.push_front(3);
	listOne.push_front(2);
	listOne.push_front(1);
 
	listOne.push_back(4);
	listOne.push_back(5);
	listOne.push_back(6);
 
	cout << "listOne.begin()--- listOne.end():" << endl;
	for (i = listOne.begin(); i != listOne.end(); ++i)
		cout << *i << " ";
	cout << endl;
 
	LISTINT::reverse_iterator ir;
	cout << "listOne.rbegin()---listOne.rend():" << endl;
	for (ir = listOne.rbegin(); ir != listOne.rend(); ir++) {
		cout << *ir << " ";
	}
	cout << endl;
 
	int result = accumulate(listOne.begin(), listOne.end(), 0);
	cout << "Sum=" << result << endl;
	cout << "------------------" << endl;
 
	//用LISTCHAR创建一个list对象
	LISTCHAR listTwo;
	//声明i为迭代器
	LISTCHAR::iterator j;
 
	listTwo.push_front('C');
	listTwo.push_front('B');
	listTwo.push_front('A');
 
	listTwo.push_back('D');
	listTwo.push_back('E');
	listTwo.push_back('F');
 
	cout << "listTwo.begin()---listTwo.end():" << endl;
	for (j = listTwo.begin(); j != listTwo.end(); ++j)
		cout << char(*j) << " ";
	cout << endl;
 
	j = max_element(listTwo.begin(), listTwo.end());
	cout << "The maximum element in listTwo is: " << char(*j) << endl;
	system("pause");
}

输出结果

3.3.4.3综合实例2

#include  
#include  
 
using namespace std;
typedef list INTLIST;
 
//从前向后显示list队列的全部元素 
void put_list(INTLIST list, char *name)
{
	INTLIST::iterator plist;
 
	cout << "The contents of " << name << " : ";
	for (plist = list.begin(); plist != list.end(); plist++)
		cout << *plist << " ";
	cout << endl;
}
 
//测试list容器的功能 
void main(void)
{
	//list1对象初始为空 
	INTLIST list1;
	INTLIST list2(5, 1);
	INTLIST list3(list2.begin(), --list2.end());
 
	//声明一个名为i的双向迭代器 
	INTLIST::iterator i;
 
	put_list(list1, "list1");
	put_list(list2, "list2");
	put_list(list3, "list3");
 
	list1.push_back(7);
	list1.push_back(8);
	cout << "list1.push_back(7) and list1.push_back(8):" << endl;
	put_list(list1, "list1");
 
	list1.push_front(6);
	list1.push_front(5);
	cout << "list1.push_front(6) and list1.push_front(5):" << endl;
	put_list(list1, "list1");
 
	list1.insert(++list1.begin(), 3, 9);
	cout << "list1.insert(list1.begin()+1,3,9):" << endl;
	put_list(list1, "list1");
 
	//测试引用类函数 
	cout << "list1.front()=" << list1.front() << endl;
	cout << "list1.back()=" << list1.back() << endl;
 
	list1.pop_front();
	list1.pop_back();
	cout << "list1.pop_front() and list1.pop_back():" << endl;
	put_list(list1, "list1");
 
	list1.erase(++list1.begin());
	cout << "list1.erase(++list1.begin()):" << endl;
	put_list(list1, "list1");
 
	list2.assign(8, 1);
	cout << "list2.assign(8,1):" << endl;
	put_list(list2, "list2");
 
	cout << "list1.max_size(): " << list1.max_size() << endl;
	cout << "list1.size(): " << list1.size() << endl;
	cout << "list1.empty(): " << list1.empty() << endl;
 
	put_list(list1, "list1");
	put_list(list3, "list3");
	cout <list3: " < list3) << endl;
	cout << "list1<list3: " << (list1 < list3) << endl;
 
	list1.sort();
	put_list(list1, "list1");
 
	list1.splice(++list1.begin(), list3);
	put_list(list1, "list1");
	put_list(list3, "list3");
	system("pause");
}

输出结果:

3.4map/multimap

map和multimap都须要#include,惟一的不同是,map的通配符key不可重复,而multimap可以,也正是因为这些区别,map支持运算符,multimap不支持运算符。在用法上没哪些区别。

C++中map提供的是一种通配符对容器,上面的数据都是成对出现的,如右图:每一对中的第一个值称之为关键字(key),每位关键字只能在map中出现一次;第二个称之为该关键字的对应值。

Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每位关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,因为这个特点,它完成有可能在我们处理一对一数据的时侯,在编程上提供快速通道。这儿说下map内部数据的组织,map内部自建一颗黑红树(一种非严格意义上的平衡二叉树),这颗树具有对数据手动排序的功能,所以在map内部所有的数据都是有序的。

3.4.1基本操作函数

begin()返回指向map背部的迭代器

clear()删掉所有元素

count()返回指定元素出现的次数

empty()假如map为空则返回true

end()返回指向map末尾的迭代器

equal_range()返回特殊条目的迭代器对

erase()删掉一个元素

find()查找一个元素

get_allocator()返回map的配置器

insert()插入元素

key_comp()返回比较元素key的函数

lower_bound()返回通配符>=给定元素的第一个位置

max_size()返回可以容纳的最大元素个数

rbegin()返回一个指向map尾部的逆向迭代器

rend()返回一个指向map背部的逆向迭代器

size()返回map中元素的个数

swap()交换两个map

upper_bound()返回通配符>给定元素的第一个位置

value_comp()返回比较元素value的函数

3.4.2申明

//头文件
#include
 
map ID_Name;
 
// 使用{}赋值是从c++11开始的,因此编译器版本过低时会报错,如visual studio 2012
map ID_Name = {
                { 2015, "Jim" },
                { 2016, "Tom" },
                { 2017, "Bob" } };

3.4.3迭代器

共有八个获取迭代器的函数:*begin,end,rbegin,rend*以及对应的*cbegin,cend,crbegin,crend*。

两者的区别在于,前者一定返回const_iterator,而后者则依照map的类型返回iterator或则const_iterator。const情况下,不容许对值进行更改。如下边代码所示:

map::iterator it;
map mmap;
const map const_mmap;
 
it = mmap.begin(); //iterator
mmap.cbegin(); //const_iterator
 
const_mmap.begin(); //const_iterator
const_mmap.cbegin(); //const_iterator

返回的迭代器可以进行加减操作,再者,假如map为空,则begin=end。

C++ STL 容器和迭代器_STL C++ 数据结构和算法_linux c++定时器

3.4.4插入操作

3.4.4.1用insert插入pair数据

//数据的插入--第一种:用insert函数插入pair数据  
#include     
#include   
#include   
using namespace std;  
  
int main()  
{  
    map mapStudent;  
    mapStudent.insert(pair(1, "student_one"));  
    mapStudent.insert(pair(2, "student_two"));  
    mapStudent.insert(pair(3, "student_three"));  
    map::iterator iter;  
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)  
       cout<first<<' '<second<<endl;  
}  

3.4.4.2用insert函数插入value_type数据

//第二种:用insert函数插入value_type数据,下面举例说明  
  
#include     
#include     
#include     
using namespace std;  
  
int main()    
{    
    map mapStudent;    
    mapStudent.insert(map::value_type (1, "student_one"));    
    mapStudent.insert(map::value_type (2, "student_two"));    
    mapStudent.insert(map::value_type (3, "student_three"));    
    map::iterator iter;    
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)  
         cout<first<<' '<second<<endl;    
}  

3.4.4.3用insert函数进行多个插入

insert共有4个重载函数:

// 插入单个键值对,并返回插入位置和成功标志,插入位置已经存在值时,插入失败
pair insert (const value_type& val);
 
//在指定位置插入,在不同位置插入效率是不一样的,因为涉及到重排
iterator insert (const_iterator position, const value_type& val);
 
// 插入多个
void insert (InputIterator first, InputIterator last);
 
//c++11开始支持,使用列表插入多个   
void insert (initializer_list il);

下边是具体使用示例:

#include 
#include 
 
int main()
{
    std::map mymap;
 
    // 插入单个值
    mymap.insert(std::pair('a', 100));
    mymap.insert(std::pair('z', 200));
 
    //返回插入位置以及是否插入成功
    std::pair<std::map::iterator, bool> ret;
    ret = mymap.insert(std::pair('z', 500));
    if (ret.second == false) {
        std::cout << "element 'z' already existed";
        std::cout << " with a value of " <second << 'n';
    }
 
    //指定位置插入
    std::map::iterator it = mymap.begin();
    mymap.insert(it, std::pair('b', 300));  //效率更高
    mymap.insert(it, std::pair('c', 400));  //效率非最高
 
    //范围多值插入
    std::map anothermap;
    anothermap.insert(mymap.begin(), mymap.find('c'));
 
    // 列表形式插入
    anothermap.insert({ { 'd', 100 }, {'e', 200} });
 
    return 0;
}

3.4.4.4用链表形式插入数据

//第三种:用数组方式插入数据,下面举例说明  
  
#include     
#include     
#include     
using namespace std;  
  
int main()    
{    
    map mapStudent;    
    mapStudent[1] = "student_one";   
    mapStudent[2] = "student_two";    
    mapStudent[3] = "student_three";    
    map::iterator iter;    
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)    
        cout<first<<' '<second<<endl;    
}  

以上三种用法,尽管都可以实现数据的插入,并且它们是有区别的,其实了第一种和第二种在疗效上是完成一样的,用insert函数插入数据,在数据的插入上涉及到集合的惟一性这个概念,即当map中有这个关键字时,insert操作是插入数据不了的linux c++定时器,并且用链表形式就不同了,它可以覆盖先前该关键字对应的值,用程序说明

mapStudent.insert(map::value_type(1,”student_one”));

mapStudent.insert(map::value_type(1,”student_two”));

里面这两条句子执行后,map中1这个关键字对应的值是“student_one”,第二条句子并没有生效,这么这就涉及到我们如何晓得insert句子是否插入成功的问题了,可以用pair来获得是否插入成功,程序如下

pair::iterator,bool>Insert_Pair;

Insert_Pair=mapStudent.insert(map::value_type(1,”student_one”));

我们通过pair的第二个变量来晓得是否插入成功,它的第一个变量返回的是一个map的迭代器,假如插入成功的话Insert_Pair.second应当是true的,否则为false。

下边给出完成代码,演示插入成功与否问题

//验证插入函数的作用效果  
#include     
#include     
#include     
using namespace std;  
  
int main()    
{  
    map mapStudent;  
    pair<map::iterator, bool> Insert_Pair;    
    Insert_Pair = mapStudent.insert(pair(1, "student_one"));    
    if(Insert_Pair.second == true)  
        cout<<"Insert Successfully"<<endl;    
    else    
        cout<<"Insert Failure"<<endl;    
    Insert_Pair = mapStudent.insert(pair(1, "student_two"));    
    if(Insert_Pair.second == true)    
        cout<<"Insert Successfully"<<endl;    
    else    
        cout<<"Insert Failure"<<endl;    
    map::iterator iter;   
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)    
       cout<first<<' '<second<<endl;    
}  

你们可以用如下程序,看下用链表插入在数据覆盖上的疗效

//验证数组形式插入数据的效果   
#include     
#include     
#include     
using namespace std;  
  
int main()    
{    
    map mapStudent;    
    mapStudent[1] = "student_one";    
    mapStudent[1] = "student_two";    
    mapStudent[2] = "student_three";    
    map::iterator iter;    
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)    
       cout<first<<' '<second<<endl;  
}  

3.4.5查找、删除、交换

查找

// 关键字查询,找到则返回指向该关键字的迭代器,否则返回指向end的迭代器
// 根据map的类型,返回的迭代器为 iterator 或者 const_iterator
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;

删掉

// 删除迭代器指向位置的键值对,并返回一个指向下一元素的迭代器
iterator erase( iterator pos )
 
// 删除一定范围内的元素,并返回一个指向下一元素的迭代器
iterator erase( const_iterator first, const_iterator last );
 
// 根据Key来进行删除, 返回删除的元素数量,在map里结果非0即1
size_t erase( const key_type& key );
 
// 清空map,清空后的size为0
void clear();

交换

// 就是两个map的内容互换
void swap( map& other );

3.4.6容量

// 查询map是否为空
bool empty();
 
// 查询map中键值对的数量
size_t size();
 
// 查询map所能包含的最大键值对数量,和系统和应用库有关。
// 此外,这并不意味着用户一定可以存这么多,很可能还没达到就已经开辟内存失败了
size_t max_size();
 
// 查询关键字为key的元素的个数,在map里结果非0即1
size_t count( const Key& key ) const; //

3.4.7排序

map中的元素是手动按Key倒序排序,所以不能对map用sort函数;

这儿要讲的是一点比较深奥的用法了,排序问题,STL中默认是采用大于号来排序的,以上代码在排序上是不存在任何问题的,由于里面的关键字是int型,它本身支持大于号运算,在一些特殊情况,例如关键字是一个结构体或则自定义类,涉及到排序都会出现问题,由于它没有大于号操作,insert等函数在编译的时侯过不去linux c++定时器,下边给出两个方式解决这个问题。

3.4.7.1大于号<重载

#include   
#include   
#include   
using namespace std;
 
typedef struct tagStudentinfo
{
	int      niD;
	string   strName;
	bool operator < (tagStudentinfo const& _A) const
	{     //这个函数指定排序策略,按niD排序,如果niD相等的话,按strName排序  
		if (niD < _A.niD) return true;
		if (niD == _A.niD)
			return strName.compare(_A.strName) < 0;
		return false;
	}
}Studentinfo, *PStudentinfo; //学生信息  
 
int main()
{
	int nSize;   //用学生信息映射分数  
	mapmapStudent;
	map::iterator iter;
	Studentinfo studentinfo;
	studentinfo.niD = 1;
	studentinfo.strName = "student_one";
	mapStudent.insert(pair(studentinfo, 90));
	studentinfo.niD = 2;
	studentinfo.strName = "student_two";
	mapStudent.insert(pair(studentinfo, 80));
	for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
		cout <first.niD << ' ' <first.strName << ' ' <second << endl;
	return 0;
}

3.4.7.2仿函数的应用,这个时侯结构体中没有直接的大于号重载

//第二种:仿函数的应用,这个时候结构体中没有直接的小于号重载,程序说明  
 
#include   
#include   
#include   
using namespace std;
 
typedef struct tagStudentinfo
{
	int      niD;
	string   strName;
}Studentinfo, *PStudentinfo; //学生信息  
 
class sort
{
public:
	bool operator() (Studentinfo const &_A, Studentinfo const &_B) const
	{
		if (_A.niD < _B.niD)
			return true;
		if (_A.niD == _B.niD)
			return _A.strName.compare(_B.strName) < 0;
		return false;
	}
};
 
int main()
{   
	//用学生信息映射分数  
	mapmapStudent;
	map::iterator iter;
	Studentinfo studentinfo;
	studentinfo.niD = 1;
	studentinfo.strName = "student_one";
	mapStudent.insert(pair(studentinfo, 90));
	studentinfo.niD = 2;
	studentinfo.strName = "student_two";
	mapStudent.insert(pair(studentinfo, 80));
	for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
		cout <first.niD << ' ' <first.strName << ' ' <second << endl;
	system("pause");
}

3.4.8unordered_map

在c++11标准前,c++标准库中只有一种map,而且它的底层实现并不是适宜所有的场景,假如我们须要其他适宜的map实现就不得不使用诸如boost库等三方的实现,在c++11中加了一种mapunordered_map,unordered_set,她们的实现有哪些不同呢?

map底层采用的是黑红树的实现查询的时间复杂度为O(logn),看上去并没有unordered_map快,而且也要看实际的数据量,即使unordered_map的查询从算法上剖析比map快,而且它有一些其它的消耗,例如哈希函数的构造和剖析,还有倘若出现哈希冲突解决哈希冲突等等都有一定的消耗,因而unordered_map的效率在很大的程度上由它的hash函数算法决定,而黑红树的效率是一个稳定值。

unordered_map的底层采用哈希表的实现,查询的时间复杂度为是O(1)。所以unordered_map内部就是无序的,数据是按散列函数插入到槽上面去的,数据之间无次序可言,若果我们不须要内部有序,这些实现是没有问题的。unordered_map属于关联式容器,采用std::pair保存key-value方式的数据。用法与map一致。非常的是,STL中的map由于是有序的二叉树储存,所以对key值须要有大小的判定,当使用外置类型时,无需重载operator<;并且用用户自定义类型的话,就须要重载operator<。unoredered_map全程使用不须要比较元素的key值的大小,而且,对于元素的==要有判定,又由于须要使用hash映射,所以,对于非内部类型,须要程序员为其定义这两者的内容,对于内部类型,就不须要了。unordered库使用“桶”来储存元素,散列值相同的被储存在一个桶里。当散列容器中有大量数据时,同一个桶里的数据也会增多,导致访问冲突,增加性能。为了提升散列容器的性能,unordered库会在插入元素是手动降低桶的数目,不须要用户指定。而且,用户也可以在构造函数或则rehash()函数中,指定最小的桶的数目。

还有另外一点从占用显存上来说由于unordered_map才用hash结构会有一定的显存损失,它的显存占用回低于map。

最后就是他们的场景了,首先假如你须要对map中的数据排序,就首选map,他会把你的数据根据key的自然排序排序(因为它的底层实现黑红树机制所以会排序),倘若不须要排序,就看你对显存和cpu的选择了,不过通常还会选择unordered_map,它的查找效率会更高。

至于使用方式和函数,二者差不多,因为篇幅限制这儿不再赘言,unordered_multimap用法亦可类推。

3.5set/multiset

std::set是关联容器,富含Key类型对象的已排序集。用比较函数compare进行排序。搜索、移除和插入拥有对数复杂度。set一般以黑红树实现。

set容器内的元素会被手动排序,set与map不同,set中的元素即是通配符又是实值长春linux培训,set不容许两个元素有相同的通配符。不能通过set的迭代器去更改set元素,缘由是更改元素会破坏set组织。当对容器中的元素进行插入或则删掉时,操作之前的所有迭代器在操作以后仍然有效。

因为set元素是排好序的,且默认为降序,因而当set集合中的元素为结构体或自定义类时,该结构体或自定义类必须实现运算符‘

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复