STL之容器、迭代器及算法知识总结

STL的关键组件包括容器(container)、迭代器(iterator)及算法(algorithm),本文就这3部分内容进行总结。

容器按照类型分为序列式容器和关联式容器,其中序列式容器包括vector、deque、list等可序群集,关联式容器包括set、multiset、map、multimap等。

vector实现采用动态数组方式,可以直接存取任意元素,支持在尾部插入和删除元素且实现速度快,在数组前端和内部插入和删除元素效率较低,因为涉及到修改元素后面的所有元素位置发生改变,具体使用实例如下所示:

#include <iostream>
#include <vector>
using namespace std;
int main(){	
	vector<char> a;
	for(int i=0;i<5;i++){
		a.push_back('a'+i);
	}
	for(int i=0;i<5;i++){
		cout<<a[i]<<" ";
	}
	cout<<endl;
	vector<int> b(5,19);
	b.insert(b.begin(),17);
	for(int i=0;i<6;i++){
		cout<<b[i]<<" ";
	}
	cout<<endl;
	return 0;	
}

deque是double-ended queue的缩写,表示双端队列,也是采用动态数组实现,支持随机存取任何元素,可以在前端和尾部插入元素且效率高,在内部插入元素需要移动其他元素效率较低,具体使用实例如下所示:

#include <iostream>
#include <deque>
using namespace std;
int main(){	
	deque<char> a;
	for(int i=0;i<5;i++){
		a.push_back('a'+i);
		a.push_front('H'+i);
	}
	for(int i=0;i<10;i++){
		cout<<a[i]<<" ";
	}
	cout<<endl;
	deque<int> b(5,19);
	b.insert(b.begin()+2,17);
	b.insert(b.end()-3,16);
	for(int i=0;i<7;i++){
		cout<<b[i]<<" ";
	}
	cout<<endl;
	return 0;	
}


list为双向链表,每个元素内存保存有数据及前后元素指针,不支持随机存取,只能通过遍历的方式访问元素,其优点在于在list内部插入或删除元素效率较高,只需改变前节点的next和后节点的prev,其他元素不受影响,因此效率较高,具体使用实例如下:

#include <iostream>
#include <list>
using namespace std;
int main(){	
	list<char> a;
	for(int i=0;i<5;i++){
		a.push_back('a'+i);
		a.push_front('H'+i);
	}
	for(list<char>::iterator it=a.begin();it!=a.end();it++){
		cout<<*it<<" ";
		
	}
	a.erase(a.begin(),a.end());
	cout<<endl<<a.size();
	cout<<endl;
	list<int> b(5,19);
	b.insert(++b.begin(),17);
	b.insert(--b.end(),16);
	for(list<int>::iterator it=b.begin();it!=b.end();it++){
		cout<<*b.begin()<<" ";
		b.pop_front();	
		b.pop_back();	
	}
	cout<<endl;
	return 0;	
}

此外,string和array虽然不是容器类,但是也可以视为STL容器来使用STL算法。

关联式容器依据特定的排序准则,自动为其元素排序,排序准则以函数形式实现,比较值或者键,缺省情况下采用operator<来比较,通常采用二叉树结构存储,节点的左子树小于该元素,右子树大于该元素。关联式容器不支持push_back、push_front、pop_front、pop_back等操作,因为其是已序序列,程序员不能自己给其设定位置,插入只需使用insert,删除使用erase。set中每个元素存储一个变量,而map存储一个pair对组(2个变量),multi-前缀主要用来说明元素是可以重复的。具体的使用实例如下所示:

#include <iostream>
#include <set>
//#include <multiset>
#include <map>
//#include <multimap>
using namespace std;
int main(){	
	set<char> a;
	for(int i=0;i<5;i++){
		a.insert('a'-i);
		a.insert('a'-i);
	}
	for(set<char>::iterator it=a.begin();it!=a.end();it++){
		cout<<*it<<" ";
	}
	cout<<endl;
	multiset<char> b;
	
		b.insert('s');
		b.insert('a');
		b.insert('s');
		b.insert('h');
		b.insert('e');
	
	for(set<char>::iterator it=b.begin();it!=b.end();it++){
		cout<<*it<<" ";
	}
	cout<<endl;
	map<int,string> c;
	
		c.insert(make_pair(5,"hello"));		
		c.insert(make_pair(4,"nihao"));
		c[3]="good afternoon";
		c[5]="hello!";
		c[3]="good afternoon";
		c[3]="good";
		c[6]="nihao";
		for(map<int,string>::iterator it=c.begin();it!=c.end();it++){
			cout<<it->first<<" "<<it->second<<endl;
		}
		map<string,int> e;
		e["haha"]=78;
	multimap<string,int> d;
	d.insert(make_pair("alex",111));
	d.insert(make_pair("alex",111));
	d.insert(make_pair("alex",101));
	d.insert(make_pair("ace",111));
	//d["James"]=23;
	//d["alex"]=111;
	//d["alex"]=123;
	for(multimap<string,int>::const_iterator it=d.begin();it!=d.end();it++){
			cout<<it->first<<" "<<it->second<<endl;
		}
	cout<<endl;
	d.erase("alex");
	for(multimap<string,int>::const_iterator it=d.begin();it!=d.end();it++){
			cout<<it->first<<" "<<it->second<<endl;
		}	
	return 0;	
}


由以上可以测试得知,不存在multimap与multiset头文件,使用它们时只需包含map与set头文件即可,map描述了一对一的关系,键不能重复,multimap描述了多对多的关系,键可以重复。map支持operator[]操作符,而multimap因为其键不唯一,由于不确定性所以不支持operator[]操作,可以看出关联式容器给自动排序,erase函数可以根据键和迭代器。

迭代器分为两类:双向迭代器和随机存取迭代器,list、map、multimap、set、multiset等提供的是双向迭代器,可以双向前进即operator++、operator--,但不能随机访问即operator+、operator-。vector、deque、string等提供的是随机存取迭代器,支持随机访问。

为了处理容器内的元素,STL提供了一些标准算法,包括搜寻、排序、拷贝、重新排序、修改、数值运算等,算法并非容器的成员函数,而是一种搭配迭代器使用的全局函数,因此只需提供一份算法,可应用于所有容器类,因此降低了代码的数量,这也是泛型编程的思想即数据结构与函数分离。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){	
	vector<int> x;
	x.push_back(2);
	x.push_back(5);
	x.push_back(3);
	x.push_back(6);
	x.push_back(1);
	x.push_back(4);
	vector<int>::iterator it;
	it=min_element(x.begin(),x.end());
	cout<<*it<<endl;
	it=max_element(x.begin(),x.end());
	cout<<*it<<endl;
	for(it=x.begin();it!=x.end();it++){
		cout<<*it<<endl;
	}
	sort(x.begin(),x.end());
	for(it=x.begin();it!=x.end();it++){
		cout<<*it<<endl;
	}
	it=find(x.begin(),x.end(),3);
	reverse(it,x.end());
	for(it=x.begin();it!=x.end();it++){
		cout<<*it<<endl;
	}
	return 0;	
}
从以上程序可以看出,min_element和max_element通过传入两个迭代器指针,获得迭代器之间的最小和最大元素的迭代器指针,使用sort对选定的两个迭代器之间的元素进行排序,find函数在指定的区间查找某个元素并返回其所在迭代器指针,reverse函数翻转指定迭代器范围内的所有元素。STL函数规定采用相同的接口表示范围,这种范围使用前闭后开区间表示,且前后顺序不能颠倒。当处理两个区间时,只需一个区间前后两端迭代器,另一个区间只需前端迭代器。

if(equal(x.begin(),x.end(),y.begin())){
		cout<<"true"<<endl;		
	}
	else
	cout<<"false"<<endl;
	copy(x.begin(),x.end(),y.begin());
	for(it=y.begin();it!=y.end();it++){
		cout<<*it<<endl;
	}
	x.resize(3);
	for(it=x.begin();it!=x.end();it++){
		cout<<*it<<endl;
	}
以上程序为equal、copy、resize等函数的测试实例。迭代器配接器包括安插性迭代器(Insert iterator)、流迭代器(Stream iterator)、逆向迭代器(Reverse iterator)。

首先介绍3种安插性迭代器back_inserter、front_inserter、inserter,如下图所示:


#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
int main(){<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>deque<int> x;
<span style="white-space:pre">	</span>x.push_back(2);
<span style="white-space:pre">	</span>x.push_back(5);
<span style="white-space:pre">	</span>x.push_back(3);
<span style="white-space:pre">	</span>x.push_back(6);
<span style="white-space:pre">	</span>x.push_back(1);
<span style="white-space:pre">	</span>x.push_back(4);
<span style="white-space:pre">	</span>deque<int> y;
<span style="white-space:pre">	</span>deque<int>::iterator it;
<span style="white-space:pre">	</span>cout<<"x: ";
<span style="white-space:pre">	</span>for(it=x.begin();it!=x.end();it++){
<span style="white-space:pre">		</span>cout<<*it<<" ";
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>cout<<endl;
<span style="white-space:pre">	</span>y.resize(x.size());
<span style="white-space:pre">	</span>copy(x.begin(),x.end(),y.begin());
<span style="white-space:pre">	</span>cout<<"y: ";
<span style="white-space:pre">	</span>for(it=y.begin();it!=y.end();it++){
<span style="white-space:pre">		</span>cout<<*it<<" ";
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>cout<<endl;
<span style="white-space:pre">	</span>y.push_back(0);
<span style="white-space:pre">	</span>cout<<"push(0) y: ";
<span style="white-space:pre">	</span>for(it=y.begin();it!=y.end();it++){
<span style="white-space:pre">		</span>cout<<*it<<" ";
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>cout<<endl;
<span style="white-space:pre">	</span>copy(x.begin(),x.end(),inserter(y,y.begin()+1));
<span style="white-space:pre">	</span>cout<<"inserter: ";
<span style="white-space:pre">	</span>for(it=y.begin();it!=y.end();it++){
<span style="white-space:pre">		</span>cout<<*it<<" ";
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>cout<<endl;
<span style="white-space:pre">	</span>copy(x.begin(),x.end(),back_inserter(y));
<span style="white-space:pre">	</span>cout<<"back_inserter: ";
<span style="white-space:pre">	</span>for(it=y.begin();it!=y.end();it++){
<span style="white-space:pre">		</span>cout<<*it<<" ";
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>cout<<endl;
<span style="white-space:pre">	</span>copy(x.begin(),x.end(),front_inserter(y));
<span style="white-space:pre">	</span>cout<<"front_inserter: ";
<span style="white-space:pre">	</span>for(it=y.begin();it!=y.end();it++){
<span style="white-space:pre">		</span>cout<<*it<<" ";
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>cout<<endl;<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>return 0;<span style="white-space:pre">	</span>
}

以上的测试实例可以看出,copy容器时,需resize容器的大小,否则拷贝失败,调用copy函数时,若第三个参数不直接选择目标容器的迭代器指针,也可使用安插性迭代器,使用inserter时,即调用insert函数,所以还需给出插入位置迭代器指针,使用back_inserter时,即调用push_back,同理使用front_inserter时,即调用push_front。不过要注意,map、set等关联性容器不支持push_back和push_front操作,所以只能使用inserter,vector不能使用push_front,所以。。

流迭代器包括istream_iterator和ostream_iterator两种,具体使用实例如下所示:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main(){	
	vector<string> coll;
	copy(istream_iterator<string> (cin),istream_iterator<string> (),back_inserter(coll));
	sort(coll.begin(),coll.end());
	unique_copy(coll.begin(),coll.end(),ostream_iterator<string> (cout,"\n"));
	return 0;	
}

注意,使用流迭代器时需要包含头文件<iterator>,这点与安插性迭代器不同,此外,unique_copy函数去除了相邻的相同元素,因此一般搭配sort函数一起使用。

除了安插性迭代器和流迭代器,还有一个逆向迭代器,主要是容器内预定义的rbegin和rend函数,注意rbegin和end-1相等,rend和begin-1相等。

下面介绍更易型算法,remove函数的程序实例如下:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main(){	
	vector<string> coll;
	coll.push_back("nihao");
	coll.push_back("hello");
	coll.push_back("how are you");
	vector<string>::iterator end=remove(coll.begin(),coll.end(),"hello");
	cout<<distance(end,coll.end())<<endl;
	copy(coll.begin(),coll.end(),ostream_iterator<string>(cout,"\n"));
	cout<<coll.size()<<endl;
	copy(coll.begin(),end,ostream_iterator<string>(cout,"\n"));
	coll.erase(end,coll.end());
	for(vector<string>::iterator it=coll.begin();it!=coll.end();it++){
		cout<<*it<<endl;
	}
	return 0;	
}

remove函数将移除的元素放到了容器的尾部,并不改变容器的大小和begin及end迭代器指针,返回一个新的end迭代器,可以利用distance函数计算随机访问迭代器的距离,若想彻底删除元素可使用容器的erase成员函数。关联式容器是已序的,所以不能使用remove,删除只能用erase函数。基本容器类都定义了自己的更易型成员函数如erase,所以尽量使用容器类定义的成员函数,防止出现意外情况。

一天的时间,STL正式入门阶段,加油。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值