跟我学c++中级篇——STL算法之比较和极值

本文介绍了如何通过C++ STL库中的max, min, minmax, max_element和clamp等函数实现极值和比较操作。通过实例展示了如何在实际编程中使用这些算法,包括标准库源码解析,并探讨了编程中库抽象的重要性。

一、极值和比较

在人们刚刚有意识时,就开始有意识的进行比较和最大最小值的操作,比如一个孩子很轻易的可以从一堆苹果中打出一个最大的,从好多的球中找到一个最小的。在和别人的玩耍过程中,知道谁跑得比谁快,谁长得比谁高等等。人类这种意识能不能让机器获得?有谁更好的方式和方法。
数学给了答案,然后进一步应用到计算机中。

二、STL算法的相关算法

在STL中的相关的算法求极值的有:
std::max:求最大值
std::min:求最小值
std::minmax:求最大最小值,返回两个极值
std::max_element:范围内的最大值
std::clamp:判断夹逼位置的比较
比较相关的有:
std::equal:判断是否相等
std::lexicographical_compare:判断指定的字符串的比较结果
其它更多的相关比较和极值算法可参考https://en.cppreference.com/w/cpp/algorithm。

三、源码分析

看一下STL中典型的相关几个函数的源码:

template<class _FwdIt,
	class _Pr>
	_NODISCARD constexpr pair<_FwdIt, _FwdIt> minmax_element(_FwdIt _First, _FwdIt _Last, _Pr _Pred)
	{	// find smallest and largest elements, using _Pred
	_Adl_verify_range(_First, _Last);
	const auto _Result =
		_Minmax_element_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Pass_fn(_Pred));
	_Seek_wrapped(_Last, _Result.second);
	_Seek_wrapped(_First, _Result.first);
	return {_First, _Last};
	}
template<class _FwdIt,
	class _Pr>
	constexpr pair<_FwdIt, _FwdIt> _Minmax_element_unchecked(_FwdIt _First, _FwdIt _Last, _Pr _Pred)
	{	// find smallest and largest elements, using _Pred
	pair<_FwdIt, _FwdIt> _Found(_First, _First);

	if (_First != _Last)
		{
		while (++_First != _Last)
			{	// process one or two elements
			_FwdIt _Next = _First;
			if (++_Next == _Last)
				{	// process last element
				if (_DEBUG_LT_PRED(_Pred, *_First, *_Found.first))
					{
					_Found.first = _First;
					}
				else if (!_DEBUG_LT_PRED(_Pred, *_First, *_Found.second))
					{
					_Found.second = _First;
					}
				}
			else
				{	// process next two elements
				if (_DEBUG_LT_PRED(_Pred, *_Next, *_First))
					{	// test _Next for new smallest
					if (_DEBUG_LT_PRED(_Pred, *_Next, *_Found.first))
						{
						_Found.first = _Next;
						}
					if (!_DEBUG_LT_PRED(_Pred, *_First, *_Found.second))
						{
						_Found.second = _First;
						}
					}
				else
					{	// test _First for new smallest
					if (_DEBUG_LT_PRED(_Pred, *_First, *_Found.first))
						{
						_Found.first = _First;
						}
					if (!_DEBUG_LT_PRED(_Pred, *_Next, *_Found.second))
						{
						_Found.second = _Next;
						}
					}
				_First = _Next;
				}
			}
		}

	return (_Found);
	}
template<class _Iter1,
	class _Iter2,
	class _Pr> inline
	false_type _Equal_memcmp_is_safe(const _Iter1&, const _Iter2&, const _Pr&)
	{	// return equal optimization category for arbitrary iterators
	return {};
	}
template<class _InIt1,
	class _InIt2,
	class _Pr> inline
	bool _Equal_unchecked(const _InIt1 _First1, const _InIt1 _Last1,
		const _InIt2 _First2, _Pr _Pred)
	{	// compare [_First1, _Last1) to [_First2, ...) using _Pred, choose optimization
	return (_Equal_unchecked1(_First1, _Last1, _First2, _Pred,
		_Equal_memcmp_is_safe(_First1, _First2, _Pred)));
	}

msvc的库中乱七八糟的宏定义和检查参数太多,其实是不太适合看相关源码的,但这玩意儿确实是又是最容易获得的,只好如此。

四、实例

看一下几个相关的实例(cppreference.com):

 
#include <algorithm>
#include <iostream>
#include <vector>
#include <cmath>
 
static bool abs_compare(int a, int b)
{
    return (std::abs(a) < std::abs(b));
}
 
int main()
{
    std::vector<int> v{ 3, 1, -14, 1, 5, 9 }; 
    std::vector<int>::iterator result;
 
    result = std::max_element(v.begin(), v.end());
    std::cout << "max element at: " << std::distance(v.begin(), result) << '\n';
 
    result = std::max_element(v.begin(), v.end(), abs_compare);
    std::cout << "max element (absolute) at: " << std::distance(v.begin(), result) << '\n';
}

运行结果:

 max element at: 5
max element (absolute) at: 2

再看一个c++17以上版本的:

 #include <cstdint>
#include <algorithm>
#include <iostream>
#include <iomanip>
 
int main()
{
    std::cout << " raw   clamped to int8_t   clamped to uint8_t\n";
    for(int const v: {-129, -128, -1, 0, 42, 127, 128, 255, 256}) {
        std::cout << std::setw(04) << v
                  << std::setw(20) << std::clamp(v, INT8_MIN, INT8_MAX)
                  << std::setw(21) << std::clamp(v, 0, UINT8_MAX) << '\n';
    }
}

输出:

 raw   clamped to int8_t   clamped to uint8_t
-129                -128                    0
-128                -128                    0
  -1                  -1                    0
   0                   0                    0
  42                  42                   42
 127                 127                  127
 128                 127                  128
 255                 127                  255
 256                 127                  255

再看比较相关:

#include <algorithm>
#include <iostream>
#include <string_view>
 
constexpr bool is_palindrome(const std::string_view& s)
{
    return std::equal(s.begin(), s.begin() + s.size()/2, s.rbegin());
}
 
void test(const std::string_view& s)
{
    std::cout << "\"" << s << "\" "
        << (is_palindrome(s) ? "is" : "is not")
        << " a palindrome\n";
}
 
int main()
{
    test("radar");
    test("hello");
}

输出结果:

"radar" is a palindrome
"hello" is not a palindrome

字符串比较:

#include <algorithm>
#include <iostream>
#include <vector>
#include <random>
 
int main()
{
    std::vector<char> v1 {'a', 'b', 'c', 'd'};
    std::vector<char> v2 {'a', 'b', 'c', 'd'};
 
    std::mt19937 g{std::random_device{}()};
    while (!std::lexicographical_compare(v1.begin(), v1.end(),
                                         v2.begin(), v2.end())) {
        for (auto c : v1) std::cout << c << ' ';
        std::cout << ">= ";
        for (auto c : v2) std::cout << c << ' ';
        std::cout << '\n';
 
        std::shuffle(v1.begin(), v1.end(), g);
        std::shuffle(v2.begin(), v2.end(), g);
    }
 
    for (auto c : v1) std::cout << c << ' ';
    std::cout << "< ";
    for (auto c : v2) std::cout << c << ' ';
    std::cout << '\n';
}

输出结果:

a b c d >= a b c d 
d a b c >= c b d a 
b d a c >= a d c b 
a c d b < c d a b

以上示例均可在std::algorithm中找到。

五、总结

看到这里,可能大家都明白了,为什么现在好多编程的程序员,其实不会“编程”,因为大量的底层逻辑被抽象和总结成库了,这样,好多的程序编写起来就会不断的优化,最终就像c++的STL库一样,不断的迭代,眼看着就c++20,23等等了。没有什么可以多说的,认真学习,不断进步,不断超越自己,才是本分!
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值