STL总结

Posted RWCC

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL总结相关的知识,希望对你有一定的参考价值。

STL总结

一、STL的本质

通过前面的学习以及使用,我们对STL已经有了一定的认识。通俗说:STL是Standard Template Library(标准模板库),是高效的C++程序库,其采用泛型编程思想对常见数据结构(顺序表,链表,栈和队列,堆,二叉树,哈希)和算法(查找、排序、集合、数值运算…)等进行封装,里面处处体现着泛型编程程序设计思想以及设计模式,已被集成到C++标准程序库中。具体说:STL中包含了容器、适配器、算法、迭代器、仿函数以及空间配置器。STL设计理念:追求代码高复用性以及运行速度的高效率,在实现时使用了许多技术,因此熟悉STL不仅对我们正常使用有很大帮助,而且对自己的知识也有一定的提高。

二、STL的六大组件

容器

容器,置物之所也。STL中的容器,可以划分为两大类:序列式容器和关联式容器。

算法

算法:问题的求解步骤,以有限的步骤,解决数学或逻辑中的问题。STL中的算法主要分为两大类:与数据结构相关算法(容器中的成员函数)和通用算法(与数据结构不相干)。STL中通用算法总共有70多个,主要包含:排序,查找,排列组合,数据移动,拷贝,删除,比较组合,运算等。以下只列出了部分常用的算法:
STL算法总结
常用算法举例:
1.accumulate
该算法作用是对区间中的元素进行累加。有以下两个版本:

// 对[first, last)区间中元素在init的基础上进行累加
template <class InputIterator, class T> T accumulate ( InputIterator first, InputIterator last, T init );
// 对[fist, last)区间中元素在init的基础上按照binary_op指定的操作进行累加
template <class InputIterator, class T, class BinaryOperation> T accumulate ( InputIterator first, InputIterator last, T init,
 BinaryOperation binary_op );

注意:使用时必须添加头文件。

#include <numeric>
#include <vector>

struct Mul2

	 int operator()(int x, int y)  return x + 2 * y; 
;

int main()

	 // 对区间中的元素进行累加
	 vector<int> v 10, 20, 30 ;
	 cout << accumulate(v.begin(), v.end(), 0)<<endl;
	 
	 // 对区间中的每个元素乘2,然后累加
	 cout << accumulate(v.begin(), v.end(), 0, Mul2()) << endl;
	 return 0; 

2.count与count_if
该算法的作用是统计区间中某个元素出现的次数。

// 统计value在区间[first,last)中出现的次数
template <class InputIterator, class T>
ptrdiff_t count ( InputIterator first, InputIterator last, const T& value )

	 ptrdiff_t ret=0;
	 while (first != last) if (*first++ == value) ++ret;
	 return ret; 
 
 
// 统计满足pred条件的元素在[first, last)中的个数
template <class InputIterator, class Predicate>
ptrdiff_t count_if ( InputIterator first, InputIterator last, Predicate pred )

	 ptrdiff_t ret=0;
	 while (first != last) if (pred(*first++)) ++ret;
	 return ret; 

注意:使用时必须添加头文件。

#include <algorithm>
#include <vector>

bool IsOdd(int i) 
 return ((i % 2) == 1); 

int main() 

	 // 统计10在v1中出现的次数
	 vector<int> v1 10, 20, 30, 30, 20, 10, 10, 20 ;
	 cout << count(v1.begin(), v1.end(), 10) << endl;
	 
	 // 统计v2中有多少个偶数
	 vector<int> v20,1,2,3,4,5,6,7,8,9;
	 cout << count_if(v2.begin(), v2.end(), IsOdd) << endl;
	 return 0; 

3. find、find_if
该算法的作用是找元素在区间中第一次出现的位置

// 在[first, last)中查找value第一次出现的位置,找到返回该元素的位置,否则返回last
// 时间复杂度O(N)
template<class InputIterator, class T>
InputIterator find ( InputIterator first, InputIterator last, const T& value )

	 for ( ;first!=last; first++) if ( *first==value ) break;
	  return first;
 
// 在[first, last)中查找满足pred条件的元素第一次出现的位置,找到返回该位置,否则返回last
// 时间复杂度O(N)
template<class InputIterator, class Predicate>
InputIterator find_if ( InputIterator first, InputIterator last, Predicate pred )

	 for ( ; first!=last ; first++ ) if ( pred(*first) ) break;
	 return first; 

4. max和min
max返回两个元素中较大值,min返回两个元素中较小值。

template <class T>
const T& max(const T& a, const T& b) 

	 return (a<b)?b:a;
 

template <class T>
const T& min(const T& a, const T& b) 

	 return !(b<a)?a:b;

5. merge
该算法作用将两个有序序列合并成一个有序序列, 使用时必须包含头文件。

template <class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator merge ( InputIterator1 first1, InputIterator1 last1,
 InputIterator2 first2, InputIterator2 last2,
 OutputIterator result )

	 while (true) 
	 
		 *result++ = (*first2<*first1)? *first2++ : *first1++;
		 if (first1==last1) return copy(first2,last2,result);
		 if (first2==last2) return copy(first1,last1,result);
	 
 

#include <algorithm>
#include <vector>
#include <list>

int main()

	 vector<int> v 2, 6, 5, 8 ;
	 list<int> L 9, 3, 0, 5, 7 ;
	 
	 sort(v.begin(), v.end());
	 L.sort();
	 
	 vector<int> vRet(v.size() + L.size());
	 merge(v.begin(), v.end(), L.begin(), L.end(), vRet.begin());
	 
	 for (auto e : vRet)
	 	cout << e << " ";
	 cout << endl;
	 return 0; 

注意:
使用时必须保证区间有序
时间复杂度为O(M+N)

6.partial_sort

该算法的作用是:找TOPK

// 在区间[first, last)中找前middle-first个最小的元素,并存储在[first, middle)中
template <class RandomAccessIterator>
void partial_sort(RandomAccessIterator first, RandomAccessIterator middle,RandomAccessIterator last);

// 在[first, last)中找前middle-first个最大或者最小的元素,并存储在[first, middle)中
template <class RandomAccessIterator, class Compare>
 void partial_sort(RandomAccessIterator first, RandomAccessIterator middle,
 RandomAccessIterator last, Compare comp);

partial_sort的实现原理是:对原始容器内区间为[first, middle)的元素执行make_heap()操作构造一个最大堆,然后拿[middle, last)中的每个元素和first进行比较,first内的元素为堆内的最大值。如果小于该最大值,则互换元素位置,并对[first, middle)内的元素进行调整,使其保持最大堆序。比较完之后在对[first, middle)内的元素做一次对排序sort_heap()操作,使其按增序排列。注意,堆序和增序是不同的。因此该算法的功能实际就是:TOP-K

#include <algorithm>
#include <vector>
#include <functional>
int main()

	 // 找该区间中前4个最小的元素, 元素最终存储在v1的前4个位置
	 vector<int> v1 4, 1, 8, 0, 5, 9, 3, 7, 2, 6 ;
	 partial_sort(v1.begin(), v1.begin() + 4, v1.end());
	 
	  // 找该区间中前4个最大的元素, 元素最终存储在v1的前4个位置
	 vector<int> v2 4, 1, 8, 0, 5, 9, 3, 7, 2, 6 ;
	 partial_sort(v2.begin(), v2.begin() + 4, v2.end(), greater<int>());
	 return 0; 

  1. partition
    该算法的作用是按照条件对区间中的元素进行划分,使用时必须包含头文件。.
template <class BidirectionalIterator, class Predicate>
BidirectionalIterator partition(BidirectionalIterator first,
 BidirectionalIterator last, Predicate pred)
 
	 while (true)
	 
		 while (first!=last && pred(*first)) ++first;
		 if (first==last--) break;
		 while (first!=last && !pred(*last)) --last;
		 if (first==last) break;
		 swap (*first++,*last);
	 
	 return first; 

#include <algorithm>
#include <vector>

bool IsOdd(int i) 
 return (i % 2) == 1; 

int main() 

	 vector<int> v0,1,2,3,4,5,6,7,8,9;
	 // 将区间中元素分割成奇数和偶数两部分
	 auto div = partition(v.begin(), v.end(), IsOdd);
	 
	 // 打印[begin, div)的元素
	 for (auto it = v.begin(); it != div; ++it)
	 	cout << *it <<" ";
	 cout << endl;
	 
	 // 打印[div, end)的元素
	 for (auto it = div; it != v.end(); ++it)
	 	cout << " " << *it;
	 cout << endl;
	 
	 return 0; 

8. reverse
该算法的作用是对区间中的元素进行逆置,使用时必须包含头文件。.

template <class BidirectionalIterator>
void reverse ( BidirectionalIterator first, BidirectionalIterator last) 

	 while ((first!=last)&&(first!=--last))
	 swap (*first++,*last);

9. sort(重要)
排序在实际应用中需要经常用到,而在目前的排序中,快排平均情况下是性能最好的一种排序,但是快排也有其自身的短板,比如说:元素接近有序、元素量比较大的情况下,直接使用快排时,堪称一场灾难。因此STL中sort算法并没有直接使用快排,而是针对各种情况进行了综合考虑。下面关于sort函数分点进行说明:

  1. sort函数提供了两个版本
    sort(first, last):默认按照小于方式排序,排序结果为升序,一般用排内置类型数据sort(first, last, comp):可以通过comp更改元素比较方式,即可以指定排序的结果为升序或者降序,一般以仿函数对象和函数指针的方式提供
  2. sort并不是一种排序算法,而是将多个排序算法混合而成
  3. 当元素个数少于__stl_threshold阈值时(16),使用直接插入排序处理
  4. 当元素个数超过__stl_threshold时,考虑是否能用快排的方式排序,因为当元素数量达到一定程度,递归式的快排可能会导致栈溢出而崩,因此:
    通过__lg函数判断递归的深度
template <class Size>
inline Size __lg(Size n) 
 
	 Size k;
	 for (k = 0; n > 1; n >>= 1) ++k;
	 return k; 
 

如果递归的深度没有超过2*log2N 时,则使用快排方式排序,注意:快排时使用到了三数取中法预防分割后一边没有数据的极端情况
如果递归深度超过2log2N时,说明数据量大,递归层次太深,可能会导致栈溢出,此时使用堆排序处理。
sort面试题
10. unique
该函数的作用是删除区间中相邻的重复性元素,确保元素唯一性,注意在使用前要保证区间中的元素是有序的,才能达到真正的去重。

// 元素不相等时,用后一个将前一个元素覆盖掉
template <class ForwardIterator>
ForwardIterator unique ( ForwardIterator first, ForwardIterator last );

// 如果元素不满足pred条件时,用后一个将前一个覆盖掉
template <class ForwardIterator, class BinaryPredicate>
 ForwardIterator unique ( ForwardIterator first, ForwardIterator last,BinaryPredicate pred );
 
template <class ForwardIterator>
ForwardIterator unique ( ForwardIterator first, ForwardIterator last )

	 ForwardIterator result=first;
	 while (++first != last)
	 
		 if (!(*result == *first)) // or: if (!pred(*result,*first)) for the pred version
		 *(++result)=*first;
	 
	 return ++result; 


注意:

  1. 该函数并不是将重复性的元素删除掉,而是用后面不重复的元素将前面重复的元素覆盖掉了。
  2. 返回值是一个迭代器,指向的是去重后容器中不重复最后一个元素的下一个位置。
  3. 该函数需要配合erase才能真正的将元素删除掉
#include <algorithm>
#include <vector>
int main()

	 vector<int> v 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 8, 7, 6, 5, 4, 3, 2, 1 ;
	 auto it = unique(v.begin(), v.end());
	 
	 for (auto e : v)
	 	cout << e << " ";
	 cout << endl;
	 
	 /*
	 从打印的结果可以看出:
	 1. unique并没有将所有重复的元素删除掉,而值删除了一个9,因为unique删除的是相邻的重复
	元素,而上述元素中只有一个9重复相邻
	 2. unique删除时只是用后面元素将前面重复位置覆盖掉了,并没有达到真正删除,若要真正删
	除,还需要erase配合
	 */
	 v.erase(it, v.end());
	 
	 // 如果想将区间中所有重复性的元素删除掉,可以先对区间中的元素进行排序
	 for (auto e : v)
	 	cout << e << " ";
	 cout << endl;
	 // 先对区间中的元素进行排序,另重复的元素放在相邻位置
	  sort(v.begin(), v.end());
	 for (auto e : v)
	 	cout << e << " ";
	 cout << endl;
	 
	 // 使用unique将重复的元素覆盖掉
	 it = unique(v.begin(), v.end());
	 
	 // 将后面无效的元素移除
	 v.erase(it, v.end());
	 for (auto e : v)
	 	cout << e << " ";
	 cout << endl;
	 return 0; 

11. next_permutation和pre_permutation(重要)
next_permutation是获取一个排序的下一个排列,可以遍历全排列,prev_permutation刚好相反,获取一个排列的前一个排列, 使用时必须包含头文件

对序列 a, b, c,每一个元素都比后面的小,按照字典序列,固定a之后,a比bc都小,c比b大,它的下一个序列即为a, c, b,而a, c, b的上一个序列即为a, b, c,同理可以推出所有的六个序列为:a, b,c、a, c, b、b, a, c、b, c, a、c, a, b、c, b, a,其中a, b, c没有上一个元素,c, b, a没有下一个元素。

注意:使用时,必须保证序列是有序的。

#include <algorithm>
#include <vector>
#include <functional>
int main() 

	 // 因为next_permutation函数是按照大于字典序获取下一个排列组合的
	 // 因此在排序时必须保证序列是升序的
	 vector<int> v = 4, 1, 2, 3 ;
	 sort(v.begin(), v.end());
	 do
	 <

以上是关于STL总结的主要内容,如果未能解决你的问题,请参考以下文章

STL迭代器失效问题(待进一步补充,当前仅总结vector和list)

STL学习总结

总结stl(以后还会慢慢补上

poj3190 Stall Reservations(贪心+STL)

算法学习——STL和基本数据结构

STL总结