STL学习笔记--算法
2015-08-14 01:19 rangers 阅读(279) 评论(0) 编辑 收藏 举报关于STL算法需要注意的是:
(1) 所有STL算法被设计用来处理一个或多个迭代器区间。第一个区间通常以起点和终点表示,至于其他区间,多数情况下只需提供起点即可,其终点可自动以第一区间的元素数推导出来,故调用者必须确保区间的有效性。
(2) STL算法采用覆盖模式(overwrite),而非安插模式(insert).所以调用者必须保证目标区间拥有足够的元素区间。
(3) 所有的算法处理的都是半开区间[begin,end)。
下面逐一介绍一些常用的算法:
for_each算法
for_each(InputIterator beg, InputIterator end, UnaryProc op)
-
对区间[beg,end)每一个元素调用
op(elem)
。 -
返回op的一个副本
-
op的任何返回值都会被忽略
-
复杂度:线性。调用op numberOfElement次。
void print(int elem) { cout << elem << " "; } template<typename T> struct AddValue { private: T _theValueToAdd; public: AddValue(const T & v) : _theValueToAdd(v) { } void operator() (T & elem) { elem += _theValueToAdd; } }; //求平均值 struct MeanValue { public: MeanValue() : _num_of_elements(0) , _sum(0) { } void operator() (int elem) { _num_of_elements++; _sum += elem; } //重载double类型运算符,没什么道理,只是返回平均值 operator double() { return (static_cast<double>(_sum) / static_cast<double>(_num_of_elements)); } private: long _num_of_elements; long _sum; }; //for_each算法 vector<int> v; std::generate_n(std::back_inserter(v),10,rand); std::copy(v.begin(),v.end(),std::ostream_iterator<int>(std::cout," ")); cout << endl; //一般函数作为for_each的参数 std::for_each(v.begin(),v.end(), print); //函数对象作为for_each的参数 //给v所有元素都加上100 std::for_each(v.begin(),v.end(),AddValue<int>(100)); cout << endl; std::copy(v.begin(),v.end(),std::ostream_iterator<int>(std::cout," ")); //for_each的返回值 for_each返回op(一再算法内部被变动过的)一个副本 //返回应该是函数对象,但是被转换成了double,这个是隐式转换 double mv = std::for_each(v.begin(),v.end(),MeanValue()); cout << "\nMeanValue: " << mv << endl;
transform 算法
transform提供两种能力:
(1) 4个参数形式。把源区间的元素转换到目标区间,即复制与修改一气呵成。
OutputInterator
tansform (InputIterator sourceBeg, InputInterator sourceEnd,
OutputIterator destBeg, UnaryFunc op)
- 针对源区间[sourceBeg,sourcEnd)调用op(elem),并将结果写到destBeg起始的目标区间。
- 调用者必须确保目标区间有足够的空间。
- sourceBeg与destBeg可以相同,所以,和for_each一样,可以使用transform来变动某一序列内的元素。transform的速度稍许些慢,因为它是将操作返回值赋值给元素,而不是直接变动元素。
(2) 5个参数形式,将前两个源序列中的元素合并,并将结果写入目标区间。即将两序列的元素结合。
OutputIterator
transform(InputIterator1 source1Beg, InputIterator1 source1End,
InputIterator2 source2Beg,
OutputIterator destBeg,
BinaryFunc op)
- 针对第一个源区间[source1Beg,source1End)以及从source2Beg开始的第二个源区间的相应元素,调用op(source1Elem,source2Elem),并将结果写入以destBeg起始的目标区间。
transform与for_each的区别:
for_each()
接受一项操作,该操作返回被改动后的参数。因此该参数必须以by reference方式传递。
transform()
运用某项操作,该操作返回被改动后的参数。
transform使用实例:
string str("acCDeFG");
//transform典型应用 ,string大小写转换
std::transform(str.begin(),str.end(),str.begin(),toupper);
template<typename T>
struct Add
{
Add(const T & v)
: _theValueToAdd(v)
{
}
T operator() (const T & elem)
{
return (_theValueToAdd + elem);
}
T _theValueToAdd;
};
//使用transform实现上述for_each的AddValue功能
std::transform(v.begin(),v.end(),v.begin(),Add<int>(1000));
int arr[] = {10,1,2,4,4,7};
list<int> list1(arr,arr + sizeof(arr) / sizeof(arr[0]));
std::copy(list1.begin(),list1.end(),std::ostream_iterator<int>(std::cout," "));
//实现对list中每个数值求平方
std::transform(list1.begin(),list1.end(),list1.begin(),list1.begin(),std::multiplies<int>());
互换元素内容 swap_ranges
//将区间[beg1, end1)内的元素和从beg2开始的区间内的对应元素互换
//返回第二个区间中最后一个被交换元素的下一位置
ForwardIterator2
swap_ranges (ForwardIterator1 beg1, ForwardIterator1 end1,
ForwardIterator2 beg2)
注:如果要将相同型别的两个容器的全部元素互换,应该使用swap()
成员函数,swap性能优异,她只交换容器的内部数据,即交换某些内部指针,所有时间复杂度为常数。
非变动性算法
(1) 元素计数
//计算区间[beg, end)中元素值等于value的元素个数
difference _type
count (InputIterator beg, InputIterator end, const T& value)
//计算区间[beg, end)中令op(elem)元素判断结果为true的元素个数
difference _type
count_if (InputIterator beg, InputIterator end, UnaryPredicate op)
int arr[] = {1,2,1,3,4,8,9,8};
vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0]));
//元素计数 求vector中大于com_num的元素个数
int comp_num = 4;
int num = std::count_if(v.begin(),v.end(),std::bind(std::greater<int>(),std::placeholders::_1,comp_num));
(2) 最大值最小值
如果存在多个最大值或最小值,算法返回找到的第一个。
//返回区间[beg, end)中最小值的位置,注意返回的是位置,你要通过 operator* 来得到值
// 以operator< 进行元素比较
InputIterator
min_element (InputIterator beg, InputIterator end)
//Op用来比较两个元素,op(elem1, elem2)
InputIterator
min_element (InputIterator beg, InputIterator end, CompFunc op)
//返回区间[beg, end)中最大值的位置
InputIterator
max_element (InputIterator beg, InputIterator end)
InputIterator
max_element (InputIterator beg, InputIterator end, CompFunc op)
(3) 搜索元素
搜索第一个匹配元素
//返回区间[beg, end)中第一个元素值等于value的元素位置,如果没有找到,两种形式都返回end
InputIterator
find (InputIterator beg, InputIterator end, const T& value)
InputIterator
find_if (InputIterator beg, InputIterator end, UnaryPredicate op)
//搜寻元素 find 已序区间使用 binary_search
//关联式容器使用成员函数find()
std::find(v.begin(),v.end(),4);
(4) 区间比较
//判断区间[beg, end)内的元素是否都和以cmpBeg开头的区间内的元素相等
bool
equal (InputIterator1 beg, InputIterator1 end,
InputIterator2 cmpBeg)
//判断区间[beg, end)内的元素和以cmpBeg开头的区间内对应的元素,
//是否都能够使op(elem, cmpElem)为true
bool
equal (InputIterator1 beg, InputIterator1 end,
InputIterator2 cmpBeg, BinaryPredicate op)
//区间比较equal 典型应用 不区分大小写比较字符串 string
struct NoCaseCompareStr
{
bool operator() (char c1, char c2) const
{
return (toupper(c1) == toupper(c2));
}
};
string src("abCD");
string dst("abcd");
bool isEqual = (src.size() == dst.size()
&& std::equal(src.begin(),src.end(),dst.begin(),NoCaseCompareStr()));
移除性算法
注:移除性算法是在一个区间内移除某些元素。这些算法并不能改变元素的数量,只是以逻辑上的思考,将原本置于后面的“不移除的元素”向前移动,覆盖那些被删除的元素而已,返回新区间的逻辑终点。
//移除区间[beg, end)中每一个与value相等的元素
ForwardIterator
remove (ForwardIterator beg, ForwardIterator end,
const T& value)
//移除区间[beg, end)中每一个令op(elem)为true的元素
ForwardIterator
remove_if (ForwardIterator beg, ForwardIterator end,
UnaryPredicate op)
int arr[] = {1,2,1,3,4,8,9,8};
vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0]));
//删除所有大于4的元素
std::function<bool(int)> func = std::bind(std::greater<int>(),std::placeholders::_1,4);//绑定函数对象
//移除性算法只是返回一个新的终点,想把元素真正删除,还需要调用erase成员函数
v.erase(std::remove_if(v.begin(),v.end(),func),v.end());
注:
(1) 无法在关联式容器上使用移除型算法
(2) list提供了等效的成员函数remove,效率更高。
排序算法
关联式容器自动排序,但对全体元素进行一次性排序,通常比始终维护它们保持已序状态来得高效一些。
(1) 对所有元素排序
//排序,缺省使用 operator< 排序
void
sort (RandomAccessIterator beg, RandomAccessIterator end)
//排序,使用op(elem1, elem2)准则
void
sort (RandomAccessIterator beg, RandomAccessIterator end,
BinaryPredicate op)
//保证相等元素的原本相对次序在排序后保持不变
//换句话出,相等的元素它会按照固有的顺序,比如字典顺序排序
void
stable_sort (RandomAccessIterator beg, RandomAccessIterator end)
void
stable_sort (RandomAccessIterator beg, RandomAccessIterator end,
BinaryPredicate op)
//降序排列
std::sort(v.begin(),v.end(),std::greater<int>());
注:
不可以对list调用这些算法,list不支持随机存取迭代器,可以使用list自己提供的成员函数sort().
(2) 局部排序
//以 < 对区间[beg, end)内的元素进行排序,使区间[beg, sortEnd)内的元素有序
void
partial_sort (RandomAccessIterator beg,
RandomAccessIterator sortEnd,
RandomAccessIterator end)
//以op(elem1, elem2)对区间内元素进行排序
void
partial_sort (RandomAccessIterator beg,
RandomAccessIterator sortEnd,
RandomAccessIterator end,
BinaryPredicate op)
和sort不同,partial_sort并不对全部元素排序:一旦第一个元素至sortEnd之前的所有元素都排拖次序,就立即停止。
对sortEnd-beg个元素进行排序,也就是说,如果sortEnd-beg等于42,则该函数将有序次序中的最小值元素放在序列中的前42个位置。partial_sort完成之后,从beg到sortEnd(但不包括sortEnd)范围内的元素时有序的,已排序范围内没有元素大于sortEnd之后的元素。未排序元素之间的次序是未指定的。
(缺省升序排列)
例:
int arr[] = {1,2,1,3,4,8,9,8};
vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0]));
vector<int> v1(v);
vector<int> v2(v);
PrintElements(v1,"V1 and V2");
//局部排序 取前3个排序元素
std::partial_sort(v1.begin(),v1.begin() + 3,v1.end(),std::greater<int>());
PrintElements(v1,"PartialSort");
//全排序
std::sort(v2.begin(),v2.end(),std::greater<int>());
PrintElements(v2,"AllSort");
备注:
sort
内部采用quicksort算法
partial_sort
内部采用heapsort算法
stable_sort
内部采用mergesort算法
(3) 根据第n个元素排序
如果只需要排序后的第n各元素,或只需令最先或最后的n个元素(无序)就位,这个算法就很有用。
//对区间[beg, end)内的元素进行排序,使第n个位置上的元素就位
//也就是n之前的元素都小于等于它,n之后的元素都大于等于它
//但是n前后的元素不要求有序
void
nth_element (RandomAccessIterator beg,
RandomAccessIterator nth,
RandomAccessIterator end)
void
nth_element (RandomAccessIterator beg,
RandomAccessIterator nth,
RandomAccessIterator end,
BinaryPredicate op)
类似的算法parttition,根据某个准则,将序列中的元素分为两部分。
//将区间[beg, end)中使op(elem)为true的元素向前移动
BidirectionalIterator
partition (BidirectionalIterator beg, BidirectionalIterator end,
UnaryPredicate op)
//会保持它们之前的相对次序
BidirectionalIterator
stable_partition (BidirectionalIterator beg, BidirectionalIterator end,
UnaryPredicate op)
nth_element
和partition
的区别:
nth_element调用之后,并不精确知道第一子集和第二子集之间有什么不同,两部分都可能包含和第n各元素相等的元素。
partiton调用之后,并不知道第一和第二子集内各有多少个元素。返回的pos指出第二个子集的起点,第二子集的所有元素都不满足上述传入的准则。
已序区间算法
针对已序区间的算法,前提是源区间必须在某个排序准则下已序,有一定的性能优势。
即使迭代器并非随机存取型,也可以使用这些算法,此时算法的复杂度会降为线性。
//判断已序区间[beg, end)是否包含和value等值的元素
bool
binary_search (ForwardIterator beg, ForwardIterator end,
const T& value)
bool
binary_search (ForwardIterator beg, ForwardIterator end,
const T& value,
BinaryPredicate op)
//返回第一个"大于等于value"的元素位置,如果不存在返回end
ForwardIterator
lower_bound (ForwardIterator beg, ForwardIterator end,
const T& value)
ForwardIterator
lower_bound (ForwardIterator beg, ForwardIterator end,
const T& value,
BinaryPredicate op)
//返回第一个"大于value"的元素位置
ForwardIterator
upper_bound (ForwardIterator beg, ForwardIterator end,
const T& value)
ForwardIterator
upper_bound (ForwardIterator beg, ForwardIterator end,
const T& value,
BinaryPredicate op)
//返回"与value相等"的元素所形成的区间
pair<ForwardIterator,ForwardIterator>
equal_range (ForwardIterator beg, ForwardIterator end,
const T& value)
pair<ForwardIterator,ForwardIterator>
equal_range (ForwardIterator beg, ForwardIterator end,
const T& value,
BinaryPredicate op)
//source1: 1 2 2 4 6 7 7 9
//source2: 2 2 2 3 6 6 8 9
//Sum: 1 2 2 2 2 2 3 4 6 6 6 7 7 8 9 9
//将源区间[source1Beg, source1End)和[source2Beg, source2End)内的元素合并
//以destBeg为起点输出
OutputIterator
merge (InputIterator source1Beg, InputIterator source1End,
InputIterator source2Beg, InputIterator source2End,
Output Iterator destBeg)
OutputIterator
merge (InputIterator source1Beg, InputIterator source1End,
InputIterator source2Beg, InputIterator source2End,
OutputIterator destBeg,
BinaryPredicate op)