STL之洗牌算法——std :: random_shuffle和std :: shuffle

前言

在STL中,有两个函数,可以将容器中的数据进行洗牌——随机排序,这两个函数就是std :: random_shuffle和std :: shuffle。

 

头文件

这两个函数在头文件<algorithm>中定义,所以使用这两个函数需要包含头文件:#include <algorithm>。

 

函数原型

template<class _RanIt,
	class _Urng> inline
	void shuffle(_RanIt _First, _RanIt _Last, _Urng&& _Func)
	{	// shuffle [_First, _Last) using URNG _Func
	typedef remove_reference_t<_Urng> _Urng0;
	_Rng_from_urng<_Iter_diff_t<_RanIt>, _Urng0> _RngFunc(_Func);
	_Random_shuffle1(_First, _Last, _RngFunc);
	}
template<class _RanIt,
	class _RngFn> inline
	void random_shuffle(_RanIt _First, _RanIt _Last, _RngFn&& _RngFunc)
	{	// shuffle [_First, _Last) using random function _RngFunc
	_Random_shuffle1(_First, _Last, _RngFunc);
	}

 

函数区别

两者区别在于第三个参数不同:std::random_shuffle第三个参数使用的是std::rand()作为随机数生成的种子,用来实现每次调用函数生成的顺序不一样。而std::shuffle()函数使用_Urng作为更好的随机生成器。std::shuffle()可以看做是std::random_shuffle的升级版。

 

使用方法

#include <iostream>
#include <algorithm>
#include <vector>
#include <random>
#include <chrono>

using std::vector;
using std::cout;
using std::endl;
using std::random_shuffle;
using std::shuffle;

int random(int i)
{
	return std::rand() % i; 
}

int main()
{
	vector<int> vec = { 1, 2, 3, 4, 5, 6 };

	for_each(vec.begin(), vec.end(), [](int x) {
		cout << x << " ";
	});
	cout << endl;

	//random_shuffle, 不使用随机生成器则每次调用后产生的结果都一样
	random_shuffle(vec.begin(), vec.end());

	for_each(vec.begin(), vec.end(), [](int x) {
		cout << x << " ";
	});
	cout << endl;

	//random_shuffle, 使用随机生成器
	random_shuffle(vec.begin(), vec.end(), random);

	for_each(vec.begin(), vec.end(), [](int x) {
		cout << x << " ";
	});
	cout << endl;

	//shuffle, 使用时间作为种子,需要包含头文件<chrono>和<random>
	unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
	shuffle(vec.begin(), vec.end(), std::default_random_engine(seed));

	for_each(vec.begin(), vec.end(), [](int x) {
		cout << x << " ";
	});
	cout << endl;

	//shuffle, 使用种子
	std::random_device rd;
	shuffle(vec.begin(), vec.end(), std::default_random_engine(rd()));

	for_each(vec.begin(), vec.end(), [](int x) {
		cout << x << " ";
	});
}

程序输出:

<think>好的,用户让我介绍一下C++标准库中的常用算法。首先,我需要确认用户的基础水平,之前的对话中用户询问过tuple,现在转向算法,可能已经对STL容器有一定了解,但需要更深入的标准库函数知识。 用户可能是刚开始学习C++,或者是希望复习巩固算法部分的知识。他们可能需要一个系统且易懂的指南,涵盖常用的算法分类、示例以及使用场景。需要注意避免过于复杂的术语,保持解释清晰,适合小白理解。 接下来,我应该按照功能分类来组织这些算法,比如非修改序列操作、修改序列操作、排序与二分查找、数值操作等。每个类别下挑选最常用重要的算法,给出简短的代码示例,并说明注意事项使用场景。 同时,用户可能关心C++11/17之后的新特性,比如并行算法,但考虑到用户可能是初学者,这部分可以简要提及,重点放在基础且广泛使用的算法上。另外,需要提醒用户包含<algorithm><numeric>头文件,以及使用迭代器时的常见错误,如越界问题。 还要注意示例代码的正确性,比如在transform中正确使用lambda表达式,在sort中区分有序无序算法,以及强调谓词函数的使用方法。此外,需要指出算法与容器方法的区别,比如优先使用容器的sort方法(如list::sort)而非通用算法,因为后者可能效率不高。 最后,总结部分应强调算法的优势,如代码简洁、高效可维护性,并鼓励用户多加练习。同时,提醒性能考虑C++版本的兼容性问题,确保用户在实际开发中能够正确应用这些算法。</think>以下是C++标准库中常用算法的分类详解(适合新手快速掌握): --- ### **算法使用三要素** 1. 包含头文件:`#include <algorithm>` 或 `#include <numeric>` 2. 基于迭代器工作(`begin(), end()`) 3. 大多数不直接修改容器大小 --- ### **一、非修改序列操作** #### 1. 遍历 `for_each` ```cpp std::vector<int> v{1,2,3}; std::for_each(v.begin(), v.end(), [](int n){ std::cout << n*2 << " "; // 输出:2 4 6 }); ``` #### 2. 查找 `find/find_if` ```cpp // 查找值为3的元素 auto it = std::find(v.begin(), v.end(), 3); // 查找第一个偶数 auto it = std::find_if(v.begin(), v.end(), [](int n){ return n%2 == 0; }); ``` #### 3. 统计 `count/count_if` ```cpp int cnt = std::count(v.begin(), v.end(), 2); // 值等于2的数量 int even_cnt = std::count_if(v.begin(), v.end(), // 偶数数量 [](int n){ return n%2 == 0; }); ``` #### 4. 检查条件 `all_of/any_of/none_of` (C++11) ```cpp bool allEven = std::all_of(v.begin(), v.end(), // 是否全偶数 [](int n){ return n%2 == 0; }); ``` --- ### **二、修改序列操作** #### 1. 拷贝 `copy` ```cpp std::vector<int> dest(3); std::copy(v.begin(), v.end(), dest.begin()); ``` #### 2. 填充 `fill` ```cpp std::fill(v.begin(), v.end(), 0); // 全部填0 ``` #### 3. 转换 `transform` ```cpp std::transform(v.begin(), v.end(), v.begin(), [](int n){ return n*2; }); // 每个元素乘2 ``` #### 4. 删除 `remove/erase` 惯用法 ```cpp // 先逻辑删除,再实际擦除 auto new_end = std::remove(v.begin(), v.end(), 2); v.erase(new_end, v.end()); ``` --- ### **三、排序与二分查找** #### 1. 排序 `sort` ```cpp std::sort(v.begin(), v.end()); // 默认升序 std::sort(v.begin(), v.end(), std::greater<>());// 降序 ``` #### 2. 二分查找 `binary_search` ```cpp bool found = std::binary_search(v.begin(), v.end(), 5); ``` #### 3. 边界查找 `lower_bound/upper_bound` ```cpp // 找第一个 >=5 的元素 auto it = std::lower_bound(v.begin(), v.end(), 5); ``` --- ### **四、数值算法(需包含<numeric>)** #### 1. 累加 `accumulate` ```cpp int sum = std::accumulate(v.begin(), v.end(), 0); // 求 ``` #### 2. 内积 `inner_product` ```cpp int dot_product = std::inner_product(v1.begin(), v1.end(), v2.begin(), 0); ``` #### 3. 前缀 `partial_sum` ```cpp std::partial_sum(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); // 输入{1,2,3} 输出:1 3 6 ``` --- ### **五、其他实用算法** #### 1. 交换 `swap` ```cpp std::swap(a, b); // 交换两个变量 ``` #### 2. 最大/最小值 `max/min` (C++11支持初始化列表) ```cpp auto max_val = std::max({1,5,3}); // 返回5 ``` #### 3. 洗牌 `shuffle` (C++11) ```cpp std::random_device rd; std::mt19937 gen(rd()); std::shuffle(v.begin(), v.end(), gen); ``` --- ### **六、C++17新特性** #### 并行算法 ```cpp #include <execution> std::sort(std::execution::par, v.begin(), v.end()); // 并行排序 ``` --- ### **使用原则** 1. **优先用算法替代循环** ```cpp // 传统循环 for(auto& n : v) n *= 2; // 更推荐的方式 std::transform(v.begin(), v.end(), v.begin(), [](int n){ return n*2; }); ``` 2. **注意迭代器有效性** 操作后迭代器可能失效,需要重新获取 3. **谓词函数优化** 简单操作用lambda,复杂操作用函数对象 4. **性能考量** 多数算法时间复杂度为$O(n)$或$O(n \log n)$ --- ### **常见错误** 1. **迭代器范围不匹配** ```cpp std::copy(v1.begin(), v1.end(), v2.begin()); // 若v2空间不足会导致未定义行为 ``` 2. **误用排序状态** ```cpp std::binary_search(v.begin(), v.end(), 5); // 必须在已排序序列中使用 ``` 3. **忽略返回值** ```cpp auto it = std::find(...); if(it == v.end()) // 必须检查是否找到 ``` --- ### **最佳实践** 1. 组合使用算法: ```cpp // 删除所有偶数并排序 v.erase( std::remove_if(v.begin(), v.end(), [](int n){ return n%2 == 0; }), v.end() ); std::sort(v.begin(), v.end()); ``` 2. 利用C++20 Ranges简化(C++20新特性): ```cpp namespace ranges = std::ranges; ranges::sort(v); auto result = ranges::find(v, 5); ``` 通过熟练使用标准算法,可以显著提升代码质量,同时获得更好的可维护性性能表现。建议结合[cppreference.com](https://en.cppreference.com/w/cpp/algorithm)查阅具体算法的详细说明。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

双鱼理

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值