STL的算法中,一些很简练的算法——持续更新

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;
  }
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值