排列和组合在数学上特别的常见,同时,在编程比赛当中也会经常碰到。STL封装了特别高效的排列组合函数:next_permutation和pre_permutation,理解它们实现的思想,才能更好地运用。
1.next_permutation
实现思想:首先,从最尾端开始往前寻找两个相邻元素,令第一元素为*i,第二元素为*ii,且满足*i < *ii,找到这样一组相邻元素后,再从最尾端开始往前检验,找出第一个大于*i的元素,令为*j,将i,j元素对调,在将ii之后的所有元素颠倒。
具体实现:
template <class BidirectionalIterator>
bool next_permutation(BidirectionalIterator first,
BidirectionalIterator last) {
if (first == last) return false; //空数组
BidirectionalIterator i = first;
++i;
if (i == last) return false; //只有一个元素
i = last;
--i;
for(;;) {
BidirectionalIterator ii = i;
--i;
if (*i < *ii) { //查找一对相邻的元素,且*i < *ii
BidirectionalIterator j = last;
while (!(*i < *--j)); //从最尾端开始,查找第一个大于*i的数
iter_swap(i, j); //交换*j和*i
reverse(ii, last); //将迭代器[first, last]范围内的数据颠倒
return true;
}
if (i == first) { //进行到最前面
reverse(first, last); //重新开始排
return false;
}
}
}
template <class BidirectionalIterator>
inline void reverse(BidirectionalIterator first, BidirectionalIterator last) {
__reverse(first, last, iterator_category(first));
}
template <class RandomAccessIterator>
void __reverse(RandomAccessIterator first, RandomAccessIterator last,
random_access_iterator_tag) {
while (first < last) iter_swap(first++, --last); //将迭代器[first, last]范围内的数据颠倒
}
2.pre_permutation
实现思想:首先,从最尾端开始往前寻找两个相邻元素,令第一元素为*i,第二元素为*ii,且满足*i > *ii,找到这样一组相邻元素后,再从最尾端开始往前检验,找出第一个小于*i的元素,令为*j,将i,j元素对调,在将ii之后的所有元素颠倒。
具体实现:
template <class BidirectionalIterator>
bool prev_permutation(BidirectionalIterator first,
BidirectionalIterator last) {
if (first == last) return false; //空数组
BidirectionalIterator i = first;
++i;
if (i == last) return false; //只有一个元素
i = last;
--i;
for(;;) {
BidirectionalIterator ii = i;
--i;
if (*ii < *i) { //查找一组相邻的元素,且*i > *ii
BidirectionalIterator j = last;
while (!(*--j < *i)); //从最尾端开始,查找第一个小于*i的数
iter_swap(i, j); //交换
reverse(ii, last); //将迭代器[first, last]范围内的数据颠倒
return true;
}
if (i == first) { //进行到最前面
reverse(first, last); //从头开始排
return false;
}
}
}
template <class BidirectionalIterator>
inline void reverse(BidirectionalIterator first, BidirectionalIterator last) {
__reverse(first, last, iterator_category(first));
}
template <class RandomAccessIterator>
void __reverse(RandomAccessIterator first, RandomAccessIterator last,
random_access_iterator_tag) {
while (first < last) iter_swap(first++, --last); //将迭代器[first, last]范围内的数据颠倒
}
3.具体运用
https://leetcode.com/problems/next-greater-element-iii/
1.《STL库源码解析》------侯捷
2.《STL3.0》源码