1.在序列1中寻找序列2第一次出现的位置
在序列1中寻找序列2第一次出现的位置在 SGI_STL 中采用了以第一个序列中的每一个元素作为标志的方法向后匹配,直到匹配到第二个序列完整出现。假设第一个序列的长度为n,第二个序列的长度为m,在最坏的情况下,该算法的复杂度为 O((n-m)*m)。该算法的亮点在于写得特别简介明了。具体代码如下:
template<class InputIterator1,class InputIterator2,class Distance1,class Distance2>
InputIterator1 __search(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, Distance1*, Distance2*)
{
Distance1 d1 = _LXX::distance(first1, last1);
Distance2 d2 = _LXX::distance(first2, last2);
if (d1 < d2) //若第一个序列小于第二个序列,不可能在第一个序列中找到第二个序列
return last1;
InputIterator1 curr1 = first1;
InputIterator2 curr2 = first2;
while (curr2 != last2) //当 curr2 == last2,此时完美的找到了匹配的子序列
{
if (*curr1 = *curr2)
{
++curr1;
++curr2;
}
else
{
if (d1 == d2) //如果这两个序列同等长度,且有一个元素不等,则不用继续判断,肯定找不到
return last2;
else
{
current1 = ++first1; //以 first1 作为标志位
current2 = first2;
--d1;
}
}
}
return first1;
2.二分查找
SGI STL中的二分查找写得也很明确间接,是值得学习的代码。其中二分查找范围为[first,last),具体代码如下:
template<class RandomAccessIterator,class T,class Distance>
RandomAccessIterator __lower_bound(RandomAccessIterator first, RandomAccessIterator last, const T & value, Distance *, random_access_iterator_tag)
{
Distance len = last - first;
Distance half;
RandomAccessIterator middle;
while (len > 0)
{
half = len >> 1;
middle = first = half;
if (*middle < value)
{
first = middle + 1;
len = len - half - 1;
}
else
len = half;
}
return first;
}
3.在序列中寻找满足条件的第一个子序列的位置(n个连续小于/大于/等于某个值的子序列)
SGI STL 中寻找n个连续满足条件的序列的方法是:首先找到第一个满足条件的元素的位置,然后检查连续的 n-1 个元素是否满足要求,若满足,则返回,否则,再次寻找下一个满足该条件的第一个元素,重复上述步骤。具体代码如下:
template<class InputIterator,class Integer,class T,class BinaryPredicate>
InputIterator search_n(InputIterator first, InputIterator last, Integer count, const T& value, BinaryPredicate binary_pred)
{
if (count <= 0)
return first;
else
{
while (first != last) //找到第一个满足条件的元素
{
if (binary_pred(*first, value)) break;
++first;
}
while (first != last)
{
Integer n = count - 1; //剩余的连续 n - 1 个元素
InputIterator i = first;
++i;
while (n > 0 && i != last && binary_pred(*i, value)) //检查连续 n - 1 个是否满足条件
{
--n;
++i;
}
if (n == 0) //满足条件则返回
return first;
else
{
while (i != last) //否则,继续寻找下一个满足条件的元素
{
if (binary_pred(*i, value)) break;
++i;
}
first = i;
}
}
return last;
}
}
4.根据条件将序列分为两部分,前一部分满足条件,后一部分不满足条件
SGI STL中的 parittion 使用了前后两个迭代器同时移动进行判断,左边的迭代器找到不符合条件的位置,右边的迭代器找到符合条件的位置,然后双方交换迭代器所指内容,左右迭代器共同往中间移动一格后继续寻找各自的条件,重复直到两个迭代器重合,此时重合处总是指向不符合条件的第一个元素的位置。此时算法并不保证元素之间的稳定性。希望保持相同元素之间的稳定性可以使用 算法导论中的 partition, 即使用两个迭代器同向移动的方法。如果希望保持所有元素之间的稳定性,STL 中有一个 stable_partition 来实现该需求,其中需要新开辟一段临时空间用来存放不满足条件的元素,之后将不满足条件的元素全部 copy 到满足元素之后即可。关于Partition 代码如下:
template<class BidirectionIterator, class Predicate>
BidirectionIterator partition(BidirectionIterator first, BidirectionIterator last, Predicate pred)
{
while (true)
{
while (true) //从左往右寻找不满足条件的位置
{
if (first == last) return first;
else if (pred(*first)) ++first;
else break;
}
--last; //从上一次\满足条件的位置的前一个位置开始
while (true) //从右往左寻找满足条件的位置
{
if (first == last) return first;
else if (!pred(*last)) --last;
else break;
}
_LXX::iter_swap(first, last); //交换找到的两个位置
++first; //从上一次满足条件的位置的下一个位置开始
}
}
其中,关于保持全部元素的相对稳定性的算法如下,此处我只是摘选出了重点处理部分:
template <class ForwardIterator, class Pointer, class Predicate,
class Distance>
ForwardIterator __stable_partition_adaptive(ForwardIterator first,
ForwardIterator last,
Predicate pred, Distance len,
Pointer buffer,
Distance buffer_size) {
if (len <= buffer_size) { //当临时容器的大小足够存放所有元素时
ForwardIterator result1 = first;
Pointer result2 = buffer; //临时存储器
for ( ; first != last ; ++first)
if (pred(*first)) {
*result1 = *first;
++result1;
}
else {
*result2 = *first; //将不满足条件的元素放入临时存储
++result2;
}
copy(buffer, result2, result1); //将临时容器中的元素存入满足条件的元素后
return result1;
}
else { //当临时容器的大小不够存储所有元素时
ForwardIterator middle = first;
advance(middle, len / 2); //将序列分为两段进行处理
ForwardIterator first_cut =
__stable_partition_adaptive(first, middle, pred, len / 2,
buffer, buffer_size); //处理第一段
ForwardIterator second_cut =
__stable_partition_adaptive(middle, last, pred, len - len / 2,
buffer, buffer_size); //处理后半段
rotate(first_cut, middle, second_cut); //将前半段与后半段接起来
len = 0;
distance(middle, second_cut, len);
advance(first_cut, len);
return first_cut;
}
}