中国大学MOOC程序设计与算法(三):C++ 面向对象程序设计 第九周 标准模板库STL(二) 笔记 之 STL算法(二)

第九周 标准模板库STL(二)
1.set和multiset
2.map和multimap
3.容器适配器
4.算法(一)
5.算法(二)

5.算法(二)

(3)删除算法

删除算法会删除一个容器里的某些元素。删除算法不应作用于关联容器。
这里所说的“删除”,并不会使容器里的元素减少,不真正“删除”内存,只是覆盖掉该删除的位置,其工作过程是:
将所有应该被删除的元素看做空位子,然后用留下的元素从后往前移,依次去填空位子。
元素往前移后,它原来的位置也就算是空位子,也应由后面的留下的元素来填上。
最后,没有被填上的空位子,维持其原来的值不变。

remove:删除区间中等于某个值的元素,比较常用,remove之后输出容器的size,会发现size没有改变
remove_if:删除区间中满足某种条件的元素
remove_copy:拷贝区间到另一个区间。等于某个值的元素不拷贝
remove_copy_if:拷贝区间到另一个区间。符合某种条件的元素不拷贝
unique:删除区间中连续相等的元素,只留下一个(可自定义比较器),就是去掉重复,返回去重后最后一个元素的下一个位置,比较常用
unique_copy:拷贝区间到另一个区间。连续相等的元素,只拷贝第一个到目标区间 (可自定义比较器)
时间复杂度都是O(n)。

unique
对[first,last) 这个序列中连续相等的元素,只留下第一个。返回值是迭代器,指向元素删除后的区间的最后一个元素的后面。

template<class FwdIt>
FwdIt unique(FwdIt first, FwdIt last);

用 == 比较是否等

template<class FwdIt, class Pred>
FwdIt unique(FwdIt first, FwdIt last, Pred pr);

自定义比较器,用 pr 比较是否等

int main()
{
	int a[5] = { 1,2,3,2,5};
	int b[6] = { 1,2,3,2,5,6};
	ostream_iterator<int> oit(cout,",");
	int * p = remove(a,a+5,2);
	cout << "1) "; copy(a,a+5,oit); cout << endl;
	//输出 1) 1,3,5,2,5,  没有真的删除内存,只是移动了元素,
	cout << "2) " << p - a << endl; 
	//输出 2) 3
	vector<int> v(b,b+6);
	remove(v.begin(),v.end(),2);
	cout << "3) ";copy(v.begin(),v.end(),oit);cout << endl;
	//输出 3) 1,3,5,6,5,6,
	cout << "4) "; cout << v.size() << endl;
	//v中的元素没有减少,输出 4) 6
	return 0;
}

(4)变序算法

变序算法***改变容器中元素的顺序,但是不改变元素的值***。变序算法需要随机访问迭代器的支持,不适用于关联容器。此类算法复杂度都是O(n)的。
reverse:颠倒区间的前后次序,在源区间上操作
reverse_copy:把一个区间颠倒后的结果拷贝到另一个区间,源区间不变
rotate:将区间进行循环左移,不常用
rotate_copy:将区间以首尾相接的形式进行旋转后的结果拷贝到另一个区间,源区间不变,不常用
next_permutation:将区间改为下一个排列(可自定义比较器),有些常用,很好用
prev_permutation:将区间改为上一个排列(可自定义比较器),有些常用,很好用
random_shuffle:随机打乱区间内元素的顺序

partition:把区间内满足某个条件的元素移到前面,不满足该条件的移到后面
stable_patition:把区间内满足某个条件的元素移到前面,不满足该条件的移到后面。而且对这两部分元素,分别保持它们原来的先后次序不变

random_shuffle :随机打乱[first,last) 中的元素,伪随机,适用于能随机访问的容器。用之前要初始化伪随机数种子:srand(unsigned(time(NULL))); //#include <ctime> template<class RanIt> void random_shuffle(RanIt first, RanIt last);

reverse:颠倒区间[first,last)顺序

template<class BidIt>
void reverse(BidIt first, BidIt last);

next_permutation:求下一个排列,找到下一个排列后,会把源区间改为这个排列顺序,然后返回true。否则直接返回false。注意这个下一个排列的标准是,将所有排列组合按照逐元素比较的方式排序,在这个顺序表中当前排列的下一个就是下一个排列,可以参照下面的例子。prev_permutation也差不多。

template<class InIt>
bool next_permutaion (Init first,Init last);

next_permutation字符串例子:

#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main()
{
	string str = "231";
	char szStr[] = "324";
	while (next_permutation(str.begin(), str.end()))
	{
		cout << str << endl;
	}
	cout << "****" << endl;
	while (next_permutation(szStr,szStr + 3))
	{
		cout << szStr << endl;
	}
	sort(str.begin(),str.end());
	cout << "****" << endl;
	while (next_permutation(str.begin(), str.end()))
	{
		cout << str << endl;
	}
	return 0;
}
输出
312
321
****
342
423
432
****
132
213
231
312
321

next_permutation整型数组例子:

#include <iostream>
#include <algorithm>
#include <string>
#include <list>
#include <iterator>
using namespace std;
int main()
{
	int a[] = { 8,7,10 };
	list<int> ls(a , a + 3);
	while( next_permutation(ls.begin(),ls.end()))
	{
		list<int>::iterator i;
		for( i = ls.begin();i != ls.end(); ++i)
			cout << * i << " ";
		cout << endl;
	}
}
输出:
8 10 7
10 7 8
10 8 7

(5)排序算法

排序算法比前面的变序算法复杂度更高,一般是O(n×log(n))。排序算法需要随机访问迭代器的支持,因而不适用于关联容器和list。
sort:将区间从小到大排序(可自定义比较器),非常常用。
stable_sort:将区间从小到大排序,并保持相等元素间的相对次序(可自定义比较器)。
partial_sort:对区间部分排序,直到最小的n个元素就位(可自定义比较器),比如只排序最小的n个元素,一旦最小的n个元素排序完毕,就结束,其余的不管。
partial_sort_copy:将区间前n个元素的排序结果拷贝到别处。源区间不变(可自定义比较器)。
nth_element:对区间部分排序,使得***第n小的元素(n从0开始算)就位***,而且比它小的都在它前面,比它大的都在它后面(可自定义比较器),但前后两段区间的顺序就不管了。
make_heap:使区间成为一个“堆”(可自定义比较器)。堆排序
push_heap:将元素加入一个是“堆”区间(可自定义比较器)。堆排序
pop_heap:从 “堆”区间删除堆顶元素(可自定义比较器)。堆排序
sort_heap:将一个“堆”区间进行排序,排序结束后,该区间就是普通的有序区间,不再是 “堆”了(可自定义比较器)。堆排序

sort 快速排序

template<class RanIt>
void sort(RanIt first, RanIt last);

按升序排序。判断x是否应比y靠前,就看 x < y 是否为true

template<class RanIt, class Pred>
void sort(RanIt first, RanIt last, Pred pr);

按升序排序。判断x是否应比y靠前,就看 pr(x,y) 是否为true

#include <iostream>
#include <algorithm>
using namespace std;
class MyLess {
		public:
			bool operator()( int n1,int n2) {
					return (n1 % 10) < ( n2 % 10);
}
};
int main() {
	int a[] = { 14,2,9,111,78 };
	sort(a,a + 5,MyLess());
	int i;
	for( i = 0;i < 5;i ++)
		cout << a[i] << " ";
	cout << endl;
	sort(a,a+5,greater<int>());
	for( i = 0;i < 5;i ++)
		cout << a[i] << " ";
}
输出:
111 2 14 78 9 //按个位数大小排序
111 78 14 9 2 //greater<int>(),按降序排序

sort 实际上是快速排序,时间复杂度 O(n*log(n));平均性能最优。但是最坏的情况下,性能可能非常差。
如果要保证“最坏情况下”的性能,那么可以使用stable_sort。stable_sort 实际上是归并排序,特点是能保持相等元素之间的先后次序。
在有足够存储空间的情况下,复杂度为 n * log(n),否则复杂度为 n *log(n) * log(n)。
stable_sort 用法和 sort相同,排序算法要求随机存取迭代器的支持,所以list 不能使用排序算法,要使用list::sort。

堆排序
堆:一种二叉树,最大元素总是在堆顶上,二叉树中任何节点的子节点总是小于或等于父节点的值.
什么是堆?n个记录的序列,其所对应的关键字的序列为{k 0 , k 1 , k 2 , …, k n-1 },若有如下关系成立时,则称该记录序列构成一个堆。k i ≥k 2i+1 且 k i ≥k 2i+2 , 其中i=0, 1, …,
例如,下面的关键字序列构成一个堆。
96 83 27 38 11 9
y r p d f b k a c
堆排序的各种算法,如make_heap等,需要随机访问迭代器的支持。

make_heap 函数模板

template<class RanIt>
void make_heap(RanIt first, RanIt last);

将区间 [first,last) 做成一个堆。用 < 作比较器

template<class RanIt, class Pred>
void make_heap(RanIt first, RanIt last, Pred pr);

将区间 [first,last) 做成一个堆。用 pr 作比较器

push_heap 函数模板

template<class RanIt>
void push_heap(RanIt first, RanIt last);
template<class RanIt, class Pred>
void push_heap(RanIt first, RanIt last, Pred pr);

在[first,last-1)已经是堆的情况下,该算法能将[first,last)变成堆,时间复杂度O(log(n))。
往已经是堆的容器中添加元素,可以在每次 push_back 一个元素后,再调用 push_heap算法。

pop_heap 函数模板
取出堆中最大的元素

template<class RanIt>
void pop_heap(RanIt first, RanIt last);
template<class RanIt, class Pred>
void pop_heap(RanIt first, RanIt last, Pred pr);

将堆中的最大元素,即 * first ,移到 last –1 位置,原 * (last –1 )被移到前面某个位置,并且移动后[first,last –1)仍然是个堆。要求原[first,last)就是个堆。复杂度 O(log(n))

(6)有序区间算法

有序区间算法要求所操作的区间是已经从小到大排好序的,而且需要随机访问迭代器的支持。所以有序区间算法不能用于关联容器和list。
binary_search:折半查找,判断区间中是否包含某个元素。这里的相等不是x==y,而是x<y和x>y同时不成立,这我就不太懂为什么要这么表述了。时间复杂度log(n),最常用
includes:判断是否一个区间中的每个元素,都在另一个区间中。
lower_bound:查找最后一个不小于某值的元素的位置。时间复杂度log(n)
upper_bound:查找第一个大于某值的元素的位置。时间复杂度log(n)
equal_range:同时获取lower_bound和upper_bound。时间复杂度log(n)
merge:合并两个有序区间到第三个区间。时间复杂度O(n)

set_union:将两个有序区间的并集拷贝到第三个区间
set_intersection:将两个有序区间的交集拷贝到第三个区间
set_difference:将两个有序区间的差拷贝到第三个区间
set_symmetric_difference:将两个有序区间的对称差(并集减交集)拷贝到第三个区间
inplace_merge:将两个连续的有序区间原地合并为一个有序区间

binary_search
折半查找,要求容器已经有序且支持随机访问迭代器,返回是否找到

template<class FwdIt, class T>
bool binary_search(FwdIt first, FwdIt last, const T& val);

上面这个版本,比较两个元素x,y 大小时, 看 x < y

template<class FwdIt, class T, class Pred>
bool binary_search(FwdIt first, FwdIt last, const T& val, Pred pr);

自定义比较器,比较两个元素x,y 大小时, 若 pr(x,y) 为true,则认为x小于y

#include <vector>
#include <bitset>
#include <iostream>
#include <numeric>
#include <list>
#include <algorithm>
using namespace std;
bool Greater10(int n)
{
	return n > 10;
}
int main() {
	const int SIZE = 10;
	int a1[] = { 2,8,1,50,3,100,8,9,10,2 };
	vector<int> v(a1,a1+SIZE);
	ostream_iterator<int> output(cout," ");
	vector<int>::iterator location;
	location = find(v.begin(),v.end(),10);
	if( location != v.end()) {
		cout << endl << "1) " << location - v.begin();
	}
	location = find_if( v.begin(),v.end(),Greater10);
	if( location != v.end())
		cout << endl << "2) " << location - v.begin();
	sort(v.begin(),v.end());
	if( binary_search(v.begin(),v.end(),9)) {
		cout << endl << "3) " << "9 found";
	}
}
输出:
1) 8
2) 3
3) 9 found

lower_bound

template<class FwdIt, class T>
FwdIt lower_bound(FwdIt first, FwdIt last, const T& val);

要求[first,last)是有序的,查找[first,last)中的,最大的位置 FwdIt,使得[first,FwdIt) 中所有
的元素都比 val 小,注意这个区间左闭右开。

upper_bound

template<class FwdIt, class T>
FwdIt upper_bound(FwdIt first, FwdIt last, const T& val);

要求[first,last)是有序的,查找[first,last)中的,最小的位置 FwdIt,使得[FwdIt,last) 中所有的元素都比 val 大,注意这个区间是左闭右开。

equal_range

template<class FwdIt, class T>
pair<FwdIt, FwdIt> equal_range(FwdIt first, FwdIt last, const T& val);

要求[first,last)是有序的,返回值是一个pair, 假设为 p, 则:[first,p.first) 中的元素都比 val 小
[p.second,last)中的所有元素都比 val 大,p.first 就是lower_bound的结果,p.last 就是 upper_bound的结果。
总结一句话:因为val可能在有序数组中连续出现多次,lower_bound其实就是返回val在区间中第一次出现位置的迭代器,upper_bound返回val在区间中最后一次出现位置的下一个位置的迭代器。equal_range返回他们两个。

merge
用 < 作比较器

template<class InIt1, class InIt2, class OutIt>
OutIt merge(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2,OutIt x);

用 pr 作比较器

template<class InIt1, class InIt2, class OutIt, class Pred>
OutIt merge(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2,OutIt x, Pred pr);

把[first1,last1), [ first2,last2) 两个升序序列合并,形成第3个升序序列,第3个升序序列以 x 开头。要保证x后面有足够的空间。

includes

template<class InIt1, class InIt2>
bool includes(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2);
template<class InIt1, class InIt2, class Pred>
bool includes(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, Pred pr);

判断 [first2,last2)中的每个元素,是否都在[first1,last1)中。第一个用 <作比较器,第二个用 pr 作比较器, pr(x,y) == true说明 x,y相等。

set_difference

template<class InIt1, class InIt2, class OutIt>
OutIt set_difference(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, OutIt x);
template<class InIt1, class InIt2, class OutIt, class Pred>
OutIt set_difference(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, OutIt x,Pred pr);

求出[first1,last1)中,不在[first2,last2)中的元素,放到从x开始的地方。如果 [first1,last1) 里有多个相等元素不在[first2,last2)中,则这多个元素也都会被放入x代表的目标区间里。

set_intersection

template<class InIt1, class InIt2, class OutIt>
OutIt set_intersection(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, OutIt x);
template<class InIt1, class InIt2, class OutIt, class Pred>
OutIt set_intersection(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, OutIt x, Pred pr);

求出[first1,last1)和[first2,last2)中共有的元素,放到从 x开始的地方。若某个元素e 在[first1,last1)里出现 n1次,在[first2,last2)里出现n2次,则该元素在目标区间里出现min(n1,n2)次。

set_symmetric_difference

template<class InIt1, class InIt2, class OutIt>
OutIt set_symmetric_difference(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, OutIt x);
template<class InIt1, class InIt2, class OutIt, class Pred>
OutIt set_symmetric_difference(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, OutIt x, Pred pr);

并集减交集,把两个区间里相互不在另一区间里的元素放入x开始的地方

set_union
template<class InIt1, class InIt2, class OutIt>
OutIt set_union(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, OutIt x);
template<class InIt1, class InIt2, class OutIt, class Pred> OutIt
set_union(InIt1 first1, InIt1 last1, InIt2 first2, InIt2 last2, OutIt x, Pred
pr); 

求两个区间的并,放到以 x开始的位置。若某个元素e 在[first1,last1)里出现 n1次,在[first2,last2)里出现n2次,则该元素在目标区间里出现max(n1,n2)次。

类模板bitset

实现多个比特的标志位,

template<size_t N>
class bitset
{..
};

实际使用的时候,N是个整型常数,如:bitset<40> bst;,bst是一个由40个比特组成的对象,用bitset的函数可以方便地访问、修改任何一位。
bitset的成员函数:

bitset<N>& operator&=(const bitset<N>& rhs);
bitset<N>& operator|=(const bitset<N>& rhs);
bitset<N>& operator^=(const bitset<N>& rhs);
bitset<N>& operator<<=(size_t num);
bitset<N>& operator>>=(size_t num);
bitset<N>& set(); //全部设成1
bitset<N>& set(size_t pos, bool val = true); //设置某位
bitset<N>& reset(); //全部设成0
bitset<N>& reset(size_t pos); //某位设成0
bitset<N>& flip(); //全部翻转
bitset<N>& flip(size_t pos); //翻转某位
reference operator[](size_t pos); //返回对某位的引用
bool operator[](size_t pos) const; //判断某位是否为1
reference at(size_t pos);
bool at(size_t pos) const;
unsigned long to_ulong() const; //转换成整数
string to_string() const; //转换成“01”字符串
size_t count() const; //计算1的个数
size_t size() const;
bool operator==(const bitset<N>& rhs) const;
bool operator!=(const bitset<N>& rhs) const;
bool test(size_t pos) const; //测试某位是否为 1
bool any() const; //是否有某位为1
bool none() const; //是否全部为0
bitset<N> operator<<(size_t pos) const;
bitset<N> operator>>(size_t pos) const;
bitset<N> operator~();//取反
static const size_t bitset_size = N;

注意:第0位在最右边

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值