C++ STL之算法(C++ primer,P713)

主要包含以下内容:

  • 算法分类
  • 函数与容器方法
  • 常用函数简介
  • 案例:统计单词个数

算法分类

算法函数设计两个主要的通用部分:

  • 使用模板来提供泛型
  • 使用迭代器来访问容器中数据的通用表示

STL将算法分为4组:

  • 非修改式序列操作
    • find()
    • for_each()
  • 修改式序列操作
    • transform()
    • random_shuffle()
    • copy()
  • 排序和相关操作
    • sort()
  • 通用数字运算
    • 单独的头文件 < numeric >

算法的通用特征:

  • 就地算法:函数完成后,在原数据上存放;
  • 复制算法:函数完成后,输出到指定位置;
    • 返回类型为迭代器,迭代器指向复制后的end()

函数与容器方法

两者显著区别如下:

  • 函数为通用,但操作步骤可能较为繁琐;
  • 部分容器方法因为容器数据结构的特殊性,执行速度优于函数方法;

对比remove函数与list容器方法:

	#include <iostream>
	#include <algorithm>
	#include <list>

	/*
	**  内联函数,定义时必须要inline
	*/
	void Show(int);
	const int SIZE = 10;

	int main(){
    	using namespace std;

    	/*
    	**  定义、初始化两个链表la、lb
    	*/
    	int arr[SIZE] = {4, 5, 4, 2, 2,
                     	 3, 4, 8, 1, 4};
    	list<int> la(arr, arr + SIZE);
    	list<int> lb(la);

    	/*
    	**  输出两个链表内容:   for_each()
    	*/
    	cout << "la:\n\t";
    	for_each(la.begin(), la.end(), Show);
    	cout << endl;
    	cout << "lb:\n\t";
    	for_each(lb.begin(), lb.end(), Show);
    	cout << endl;

    	/*
    	**  利用容器方法,删除 la 中所有的元素 4, 并输出
    	*/
    	la.remove(4);

    	cout << "la.remove:\n\t";
    	for_each(la.begin(), la.end(), Show);
    	cout << endl;

    	/*
    	**  利用函数方法,删除 lb 中所有的元素 4, 并输出
    	**      因为函数无法修改list长度,所以需要两步:
    	**  1.先remove(),remove()后list元素位置被改变(注意remove的返回类型)
    	**  2.利用erase()容器方法擦除所有的元素 4
    	*/
    	auto last = remove(lb.begin(), lb.end(), 4);

    	cout << "remove:\n\t";
    	for_each(lb.begin(), lb.end(), Show);
    	cout << endl;

    	lb.erase(last, lb.end());

    	cout << "lb.erase:\n\t";
    	for_each(lb.begin(), lb.end(), Show);
    	cout << endl;
    
    	return 0;
	}

	inline void Show(int v){
    	std::cout << v << " ";
	}

程序输出为:

la:
    4 5 4 2 2 3 4 8 1 4
lb:
    4 5 4 2 2 3 4 8 1 4
la.remove:
    5 2 2 3 8 1
remove:
    5 2 2 3 8 1 4 8 1 4
lb.erase:
    5 2 2 3 8 1

对比发现:

  • 通常来说,函数方法可以进行容器内部元素位置的交换等工作,但无法修改容器的长度(删除元素等操作);

函数简介:

简要介绍几个函数的用法:

  • next_permutation
  • copy
  • replace
  • transform

next_permutation

主要是对元素进行排列组合;

  • 将当前序列置换为此集合的下一个序列。
  • 如果要生成更多序列,则返回true。
  • 如果序列是集合中的最大序列,则生成最小值并返回false。
    • 有点拗口,给个例子
    • ABC 执行一次next_permutation后,返回值为true,原数据变为 ACB;
    • CBA 执行一次next_permutation后,返回值false,数据变为 ABC;
  • 会自动跳过重复的排列;
  • 所以使用前最好进行排序;

给个例子,看一下输出:

	#include <iostream>
	#include <algorithm>
	#include <vector>

	inline void Show(const char & ch){
    	std::cout << ch << " ";
	}

	int main(){
    	using namespace std;
    	//	初始化向量
    	vector<char> vch = {'A', 'C', 'B', 'A'};
    	cout << "vch:\n\t";
    	for_each(vch.begin(), vch.end(), Show);
    	cout << endl;
		//	排序
    	sort(vch.begin(), vch.end());
    	cout << "sort:\n\t";
    	for_each(vch.begin(), vch.end(), Show);
    	cout << endl;
		//	输出排列组合
    	cout << "permutation:\n\t";
    	while (next_permutation(vch.begin(), vch.end())) {
        	for_each(vch.begin(), vch.end(), Show);
        	cout << "\n\t";
    	}

    	return 0;
	}

程序结果为:

vch:
    A C B A
sort:
    A A B C
permutation:
    A A C B		//	稍微思考一下,为什么这里少了一个 A A B C
    A B A C
    A B C A
    A C A B
    A C B A
    B A A C
    B A C A
    B C A A
    C A A B
    C A B A
    C B A A

copy

  • 利用copy输出到ostream
  • 利用copy拷贝(插入)

输出/拷贝的示例:

	#include <iostream>
	#include <algorithm>
	#include <iterator>
	#include <vector>
	#include <string>

	int main(){
    	using namespace std;

    	//  利用copy输出:
    	vector<string> vch{"cat", "dog", "monkey", "mouse"};
    	cout << "old_vch:\n\t";
    	copy(vch.begin(), vch.end(), 
         	 ostream_iterator<string, char>(cout, "\n\t"));

    	//  利用copy拷贝插入:
    	//	相当于把vch[begin, end) 插入到vch_new的begin之前
    	vector<string> vch_new;
    	copy(vch.begin(), vch.end(), 
        	 insert_iterator<vector<string> >(vch_new, vch_new.begin()));
    	
    	//	 再次输出:
    	cout << "\nvch_new:\n\t";
    	copy(vch_new.begin(), vch_new.end(),
         	 ostream_iterator<string, char>(cout, "\n\t"));
    	return 0;
	}

程序输出:

old_vch:
    cat
    dog
    monkey
    mouse

vch_new:
    cat
    dog
    monkey
    mouse

replace

  • 将容器中的所有的 A 替换成 B
  • eg:下面例子将所有的 “old” 替换成了 “new”

示例程序:

	#include <iostream>
	#include <algorithm>
	#include <iterator>
	#include <vector>
	#include <string>

	int main(){
    	using namespace std;

    	//  利用copy输出:
    	vector<string> vch{"cat", "old", "monkey", "old"};
    	cout << "old_vch:\n\t";
    	copy(vch.begin(), vch.end(), 
         	 ostream_iterator<string, char>(cout, "\n\t"));

    	//  将容器中的 "old" 替换成 "new" :
    	replace(vch.begin(), vch.end(), "old", "new");
    	
    	cout << "\nreplace:\n\t";
    	copy(vch.begin(), vch.end(), 
         	 ostream_iterator<string, char>(cout, "\n\t"));
    	return 0;
	}

程序输出:

old_vch:
    cat
    old
    monkey
    old

replace:
    cat
    new
    monkey
    new

transform

  • 两个容器之间的数学运算,容器长度必须相等;
    • eg:varr1 = [ 1,2,3,4 ],varr2 = [ 1,2,3,4 ];
    • 计算两个向量的和,积(对应位置计算,不是向量积);
  • 基本都是用于数值计算,需要用到< functional >中的函数符;
  • 可以将结果传送到任意位置(原、新位置);

示例程序:

	#include <iostream>
	#include <algorithm>
	#include <iterator>
	#include <vector>
	#include <cmath>
	#include <functional>

	int main(){
    	using namespace std;

    	vector<double> varr1 = {1.0, 2.0, 3.0, 4.0};
    	vector<double> varr2(varr1);

    	ostream_iterator<double, char> out(cout, " ");

    	//  输出varr1 与 varr2
    	cout << "varr1:\n\t";
    	copy(varr1.begin(), varr1.end(), out);
    	cout << endl;
    	
    	cout << "varr2:\n\t";
    	copy(varr2.begin(), varr2.end(), out);
    	cout << endl;

    	//  将varr1 + varr2传送到out输出;
    	cout << "1 + 2:\n\t";
    	transform(varr1.begin(), varr1.end(), varr2.begin(), out, plus<double>());
    	cout << endl;

    	//  将varr1 * varr2传送回varr1,并输出
    	transform(varr1.begin(), varr1.end(), varr2.begin(), varr1.begin(), multiplies<double>() );
    	cout << "1 * 2:\n\t";
    	copy(varr1.begin(), varr1.end(), out);
    	cout << endl;

    	return 0;
	}

程序结果:

varr1:
    	1 2 3 4
varr2:
    	1 2 3 4
1 + 2:
    	2 4 6 8
1 * 2:
    	1 4 9 16

案例:统计一串文字中单词出现的个数

具体要求如下:

  • 输入一串无标点符号的英文;
  • 将所有单词大写转换成小写并存储;
  • 统计每个单词出现的次数;
  • 按照字典序输出单词、次数;
  • 按照出现频率由高到低输出单词、次数;

测试案例:

The dog saw the cat and thought the cat fat
The cat saw the cat perfect
quit

示例程序:

	#include <iostream>
	#include <algorithm>
	#include <iterator>
	#include <vector>
	#include <string>
	#include <set>
	#include <map>
	#include <functional>
	#include <cctype>
	using namespace std;

	typedef struct{
    	string word;
    	int times;
	}Sword;

	inline char toLower(char ch) { return tolower(ch); }
	inline bool lessthanWord(Sword & swa, Sword & swb) { return swa.word < swb.word; }
	inline bool morethanTimes(Sword & swa, Sword & swb) { return swa.times > swb.times; }
	string & ToLower(string & st);
	void output(Sword & s);

	int main(){
    	//  输入words
    	vector<string> words;
    	cout <<"Enter words(quit to quit):\n";
    	string input;
    	while (cin >> input && input != "quit")
        	words.push_back(input);
    
    	//  输出words内容
    	cout << endl;
    	ostream_iterator<string, char> out(cout, " ");
    	cout << "You entered woeds:\n";
    	copy(words.begin(), words.end(), out);
    	cout << endl << endl;

    	//  转换大小写,并将其放入set中
    	set<string> wordset;
    	transform(words.begin(), words.end(), 
              	  insert_iterator<set<string> >(wordset, wordset.begin()), ToLower);
    	cout << "List of words:\n";
    	copy(wordset.begin(), wordset.end(), out);
    	cout << endl << endl;

    	//  统计次数,并放入struct中,需要用到count函数
    	vector<Sword> vSword;
    	Sword temp;
    	for (auto i = wordset.begin(); i != wordset.end(); ++i) {
        	temp.word = *i;
        	temp.times = count(words.begin(), words.end(), *i);
        	vSword.push_back(temp);
    	}

    	//  按照word排序输出:(字典序)
    	sort(vSword.begin(), vSword.end(), lessthanWord);
    	cout << "Sort by words:\n";
    	for_each(vSword.begin(), vSword.end(), output);
    	cout << endl;

    	//  按照times排序输出:(由高到底)
    	sort(vSword.begin(), vSword.end(), morethanTimes);
    	cout << "Sort by times:\n";
    	for_each(vSword.begin(), vSword.end(), output);
    	cout << endl;

    	return 0;
	}

	string & ToLower(string & st) {
    	transform(st.begin(), st.end(), st.begin(), toLower);
    	return st;
	}

	void output(Sword & s) {
    	cout << s.word << '\t' << s.times << endl;
	}

程序输出:

You entered woeds:
The dog saw the cat and thought the cat fat The cat saw the cat perfect

List of words:
and cat dog fat perfect saw the thought

Sort by words:
and     1
cat     4
dog     1
fat     1
perfect 1
saw     2
the     5
thought 1

Sort by times:
the     5	
cat     4
saw     2
and     1
dog     1
fat     1
perfect 1
thought 1
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值