STL——算法
程序员文章站
2022-07-12 17:58:36
...
目录
accumulate
- 该算法作用是对某区间中的元素进行累加,使用时必须添加头文件
<numeric>
;
// 对[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);
- 举例:
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;
}
count
与count_if
- 该算法的作用是统计某区间中某个元素出现的次数,使用时必须添加头文件
<algorithm>
;
// 统计value在区间[first,last)中出现的次数
template <class InputIterator, class T>
ptrdiff_t count (InputIterator first, InputIterator last, const T& value);
// 统计满足pred(可以是仿函数对象、函数指针)条件的元素在[first, last)中的个数
template <class InputIterator, class Predicate>
ptrdiff_t count_if (InputIterator first, InputIterator last, Predicate pred);
- 举例:
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> v2{0,1,2,3,4,5,6,7,8,9};
cout << count_if(v2.begin(), v2.end(), IsOdd()) << endl;
return 0;
}
find
与find_if
- 该算法的作用是找元素在区间中第一次出现的位置,使用时必须包含头文件
<algorithm>
;
// 在[first, last)中查找value第一次出现的位置,找到返回该元素的位置,否则返回last,时间复杂度O(N)
template<class InputIterator, class T>
InputIterator find ( InputIterator first, InputIterator last, const T& value );
// 在[first, last)中查找满足pred(可以是仿函数对象、函数指针)条件的元素第一次出现的位置,找到返回该位置,否则返回last,时间复杂度O(N)
template<class InputIterator, class Predicate>
InputIterator find_if (InputIterator first, InputIterator last, Predicate pred);
max
与min
-
max
返回两个元素中较大值,min
返回两个元素中较小值,使用时必须包含头文件<algorithm>
;
template <class T>
const T& max(const T& a, const T& b);
template <class T>
const T& min(const T& a, const T& b);
merge
- 该算法作用将两个有序序列合并成一个有序序列,使用时必须包含头文件
<algorithm>
;
template <class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator merge (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2,OutputIterator result);
- 举例:
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;
}
partial_sort
- 该算法的作用是找前 K 个最大 / 小的元素,使用时必须包含头文件
<algorithm>
;
// 在区间[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()
操作,使其按增序排列; - 举例:
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;
}
partition
- 该算法的作用是按照条件对区间中的元素进行划分,使用时必须包含头文件
<algorithm>
;
template <class BidirectionalIterator, class Predicate>
BidirectionalIterator partition(BidirectionalIterator first, BidirectionalIterator last, Predicate pred);
- 举例:
bool IsOdd(int i)
{ return (i % 2) == 1; }
int main(){
vector<int> v{0,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;
}
reverse
- 该算法的作用是对区间中的元素进行逆置,使用时必须包含头文件
<algorithm>
;
template <class BidirectionalIterator>
void reverse ( BidirectionalIterator first, BidirectionalIterator last);
sort
- 排序在实际应用中需要经常用到,而在目前的排序中,快排平均情况下是性能最好的一种排序,但是快排也有其自身的短板,比如说:元素接近有序、元素量比较大的情况下,直接使用快排时,堪称一场灾难,因此 STL 中
sort
算法并没有直接使用快排,而是针对各种情况进行了综合考虑,下面关于sort
函数分点进行说明:-
sort
函数提供了两个版本:-
sort(first, last)
:默认按照小于方式排序,排序结果为升序,一般用排内置类型数据 -
sort(first, last, comp)
:可以通过 comp 更改元素比较方式,即可以指定排序的结果为升序或者降序,一般以仿函数对象和函数指针的方式提供;
-
-
sort
并不是一种排序算法,而是将多个排序算法混合而成; - 当元素个数少于
__stl_threshold
阈值时 (这个值为 16),使用直接插入排序处理; - 当元素个数超过
__stl_threshold
时,考虑是否能用快排的方式排序,因为当元素数量达到一定程度,递归式的快排可能会导致栈溢出而崩,因此:- 通过
__lg
函数判断递归的深度; - 如果递归的深度没有超过 2 * log2N 时,则使用快排方式排序,注意:快排时使用到了三数取中法预防分割后,一边没有数据的极端情况;
- 如果递归深度超过 2 * log2N 时,说明数据量大,递归层次太深,可能会导致栈溢出,此时使用堆排序处理;
- 通过
-
unique
- 该函数的作用是删除区间中相邻的重复性元素,确保元素唯一性,注意在使用前要保证区间中的元素是有序的,才能达到真正的去重,使用时必须包含头文件
<algorithm>
; - 注意:
- 该函数并不是将重复性的元素删除掉,而是用后面不重复的元素将前面重复的元素覆盖掉了;
- 返回值是一个迭代器,指向的是去重后容器中不重复最后一个元素的下一个位置;
- 该函数需要配合
erase
才能真正的将元素删除掉;
// 元素不相等时,用后一个将前一个元素覆盖掉
template <class ForwardIterator>
ForwardIterator unique ( ForwardIterator first, ForwardIterator last);
// 如果元素不满足pred(可以是仿函数对象、函数指针)条件时,用后一个将前一个覆盖掉
template <class ForwardIterator, class BinaryPredicate>
ForwardIterator unique ( ForwardIterator first, ForwardIterator last,
BinaryPredicate pred);
- 举例:
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;
}
next_permutation
与pre_permutation
-
next_permutation
是获取一个排序的下一个排列,可以遍历全排列,prev_permutation
刚好相反,获取一个排列的前一个排列, 使用时必须包含头文件<algorithm>
; - 举例:
int main(){
// 因为next_permutation函数是按照大于字典序获取下一个排列组合的
// 因此在排序时必须保证序列是升序的
vector<int> v = {4, 1, 2, 3 };
sort(v.begin(), v.end());
do{
cout << v[0] << " " << v[1] << " " << v[2] <<" " <<v[3]<< endl;
} while (next_permutation(v.begin(), v.end()));
cout << endl;
// 因为prev_permutation函数是按照小于字典序获取下一个排列组合的
// 因此在排序时必须保证序列是降序的
//sort(v.begin(), v.end());
sort(v.begin(), v.end(), greater<int>());
do{
cout << v[0] << " " << v[1] << " " << v[2] << " " << v[3] << endl;
} while (prev_permutation(v.begin(), v.end()));
return 0;
}