STL 初步

sort用法

sort排序区间为下标范围为[n1,n2)的元素

从小到大排序:

1
sort(数组名+n1,数组名+n2);

从大到小排序:

1
sort(数组名+n1,数组名+n2,greater<type>());

自定义排序规则:

sort函数的第三个参数可以是函数对象也可以是函数指针

1.cmp

如:

1
2
3
4
5
6
7
	bool cmp (int a,int b){
return a > b;
}
bool cmp (stu a,stu b){
return a.score > b.score;
}
sort(a,a+len;cmp);

2.重载

如:

1
2
3
4
5
6
7
8
9
10
11
struct rule{
bool operator() ( const T & name1,const T & name2) const {
return name1 < name2;
}
};
struct rule {
bool operator() (const Student & s1,const Student & s2) const {
return s1.id < s2.id;
}
};
sort(a,a+len;rule());

stl里的二分查找算法

二分查找(一种写法):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<bits/stdc++.h>
using namespace std;
typedef struct x List;
struct x{
int a[10000];
int len;
}p;
int ef(List p,int find){
int left,right,mid,no=-1;
left=1;
right=p.len;
while(left<=right){
mid=(left+right)/2;
if(find<p.a[mid])
right=mid-1;
else if(find>p.a[mid])
left=mid+1;
else
return mid;
}
return no;
}
int main()
{
int num,find;
List p;
scanf("%d",&p.len);
for(int i=1;i<=p.len;i++){
scanf("%d",&p.a[i]);
}
scanf("%d",&find);
num=ef(p,find);
if(num==-1)
printf("Not found!");
else
printf("%d",p.a[num]);
return 0;
}

stlL提供在排好序的数组上进行二分查找的算法,查找区间为下标范围为[n1,n2)的元素

包括binary_search,lower_bound和upper_bound

注意:这里查找的规则是依据sort排序的规则而定的,所以不一定是值相等就找到

1
binary_search(数组名+n1,数组名+n2,值,(排序规则)); 

用upper_bound二分查找上界

1
type * upper_bound(数组名+n1,数组名+n2,值,(排序规则));

返回一个指针 type * p,表示在值后面的第一个相邻元素;

用lower_bound二分查找下界

1
type * lower_bound(数组名+n1,数组名+n2,(排序规则));

也返回一个指针 type* p (p可换),表示在值前面的第一个相邻元素;

应用举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
struct Rule {
bool operator()( const int & a1,const int & a2) const {
return a1%10 < a2%10;
}
};
void Print(int a[],int size) {
for(int i = 0;i < size;++i) {
cout << a[i] << "," ;
}
cout << endl;
}
#define NUM 7
int main()
{
int a[NUM] = { 12,5,3,5,98,21,7};
sort(a,a+NUM);
Print(a,NUM); // => 3,5,5,7,12,21,98,
int * p = lower_bound(a,a+NUM,5);
cout << *p << "," << p-a << endl; //=> 5,1
p = upper_bound(a,a+NUM,5);
cout << *p << endl; //=>7
cout << * upper_bound(a,a+NUM,13) << endl; //=>21
sort(a,a+NUM,Rule());
Print(a,NUM); //=>21,12,3,5,5,7,98,
cout << * lower_bound(a,a+NUM,16,Rule()) << endl; // => 7
cout << lower_bound(a,a+NUM,25,Rule()) - a<< endl; // => 3
cout << upper_bound(a,a+NUM,18,Rule()) - a << endl; // => 7
if( upper_bound(a,a+NUM,18,Rule()) == a+NUM)
cout << "not found" << endl; //=> not found
cout << * upper_bound(a,a+NUM,5,Rule()) << endl; // =>7
cout << * upper_bound(a,a+NUM,4,Rule()) << endl; // =>5
return 0;
}

STL中的平衡二叉树数据结构

目的是使增加数据、删除数据、查找数据都能在 log(n)复杂度完成

用到下面四种“排序容器”:multiset,set,multimap,map;

multiset

multiset用法:

1
multiset<T> st; 

定义了一个multiset变量st,st里面可以存放T类型的数据,并且能自动排序(默认a<b为true)。开始时st为空

可用 st.insert添加元素,st.find查找元素,st.erase删除元素,复杂度 都是 log(n)

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <cstring>
#include <set> //使用multiset和set需要此头文件
using namespace std;
int main()
{
multiset<int> st;
int a[10]={1,14,12,13,7,13,21,19,8,8 };
for(int i = 0;i < 10; ++i)
st.insert(a[i]); //插入的是a [i]的复制品
multiset<int>::iterator i; //迭代器,近似于指针
for(i = st.begin(); i != st.end(); ++i)
cout << * i << ",";
cout << endl;

输出:1,7,8,8,12,13,13,14,19,21

i = st.find(22); //查找22,返回值是迭代器
if( i == st.end()) //找不到则返回值为 end()
cout << "not found" << endl;
st.insert(22); //插入 22
i = st.find(22);
if( i == st.end())
cout << "not found" << endl;
else
cout << "found:" << *i <<endl;
//找到则返回指向找到的元素的迭代器

输出:
not found
found:22
i = st.lower_bound(13);
//返回最靠后的迭代器 it,使得[begin(),it)中的元素
//都在 13 前面 ,复杂度 log(n)
cout << * i << endl;
i = st.upper_bound(8);
//返回最靠前的迭代器 it,使得[it,end())中的元素
//都在 8 后面,复杂度 log(n)
cout << * i << endl;
st.erase(i); //删除迭代器 i 指向的元素,即12
for(i = st.begin(); i != st.end(); ++i)
cout << * i << ",";
return 0;
}

输出:
13
12
1,7,8,8,13,13,14,19,21,22

multiset 上的迭代器:

1
multiset<T>::iterator p; 

p是迭代器,相当于指针,可用于指向multiset中的元素。访问multiset中的元素要通过迭代器

与指针的不同:

multiset上的迭代器可 ++ ,—, 用 != 和 == 比较,不可比大小,不可加减整数,不可相减

st.begin() 返回值类型为 multiset::iterator, 是指向st中的头一个元素的迭代器

st.end() 返回值类型为 multiset::iterator, 是指向st中的最后一个元素后面的迭代器

对迭代器 ++ ,其就指向容器中下一个元素,— 则令其指向上一个元素

自定义排序规则的multiset 用法:

1
2
3
multiset<int,greater<int> > st;//排序规则为从大到小
multiset<int,Rule1> st;
multiset<Student,Rule1>::iterator p;//对应迭代器写法

set

学set前先了解一下pair模板的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pair<T1,T2>
等价于:
struct {
T1 first;
T2 second;
};
例如:pair<int, double> a;
等价于:
struct {
int first;
double second;
} a;
a.first = 1;
a.second = 93.93;

注意:first和second不能更换

set和multiset的区别在于容器里不能有重复元素

这里的重复也是依据预先定义的规则才有意义

set插入元素可能不成功(重复)

set在插入时返回的result.second如果为零,表示因重复而无法插入

1
pair<set<int>::iterator, bool> result = st.insert(2);

multimap

multimap容器里的元素,都是pair形式的

1
2
3
4
5
6
multimap<T1,T2> mp
//即都是如下类型:
struct {
T1 first; //关键字
T2 second; //值
};

multimap中的元素按照first排序,并可以按first进行查找,默认”a.first < b.first” 为true

应用举例:

一个学生成绩录入和查询系统,接受以下两种输入:

Add name id score

Query score

name是个不超过16字符的字符串,中间没有空格,代表学生姓名。id 是个整数,代表学号。score是个整数,表示分数。学号不会重复,分数 和姓名都可能重复。

两种输入交替出现。第一种输入表示要添加一个学生的信息,碰到这 种输入,就记下学生的姓名、id和分数。第二种输入表示要查询,碰到这 种输入,就输出已有记录中分数比score低的最高分获得者的姓名、学号 和分数。如果有多个学生都满足条件,就输出学号最大的那个学生的信 息。如果找不到满足条件的学生,则输出“Nobody”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
输入样例:
Add Jack 12 78
Query 78
Query 81
Add Percy 9 81
Add Marry 8 81
Query 82
Add Tom 11 79
Query 80
Query 81
输出样例:
Nobody
Jack 12 78
Percy 9 81
Tom 11 79
Tom 11 79

#include <iostream>
#include <map> //使用multimap和map需要包含此头文件
#include <cstring>
using namespace std;
struct StudentInfo {
int id;
char name[20];
};
struct Student {
int score;
StudentInfo info;
};
typedef multimap<int,StudentInfo> MAP_STD;
int main()
{
MAP_STD mp;
Student st;
char cmd[20];
while( cin >> cmd ) {
if( cmd[0] == 'A') {
cin >> st.info.name >> st.info.id >> st.score ;
mp.insert(make_pair(st.score,st.info ));
} //make_pair生成一个 pair<int,StudentInfo>变量
//其first 等于 st.score, second 等于 st.info
else if( cmd[0] == 'Q' ){
int score;
cin >> score;
MAP_STD::iterator p = mp.lower_bound (score);
if( p!= mp.begin()) {
--p;
score = p->first; //比要查询分数低的最高分
MAP_STD::iterator maxp = p;
int maxId = p->second.id;
for(; p != mp.begin() && p->first == score; --p) {
//遍历所有成绩和score相等的学生
if( p->second.id > maxId ) {
maxp = p;
maxId = p->second.id ;
}
}
if( p->first == score) {
//如果上面循环是因为 p == mp.begin() 而终止,则p指向的元素还要处理
if( p->second.id > maxId ) {
maxp = p;
maxId = p->second.id ;
}
}
cout << maxp->second.name << " " << maxp->second.id << " " << maxp->first << endl;
}
//lower_bound的结果就是 begin,说明没人分数比查询分数低
else cout << "Nobody" << endl;
}
}
return 0;
}

map

和multimap区别在于:

不能有关键字重复的元素

可以使用 [] ,下标为关键字,返回值为first和关键字相同的元素的second

当然插入元素也可能失败
[]的用法如下:

1
2
3
4
5
6
7
8
9
10
struct Student {
string name;
int score;
};
typedef map<string,int> MP;
......
Student students[5] = {{"Jack",89},{"Tom",74},{"Cindy",87},{"Alysa",87},{"Micheal",98}};
......
cout << mp["Jack"] << endl; // 输出 89
mp["Jack"] = 60; //修改名为"Jack"的元素的second

很实用的例子

单词词频统计程序

输入大量单词,每个单词,一行,不超过20字符,没有空格。 按出现次数从多到少输出这些单词及其出现次数 。出现次数相同的,字典序靠前的在前面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
输入样例:
this
is
ok
this
plus
that
is
plus
plus
输出样例:
plus 3
is 2
this 2
ok 1
that 1

首先你要知道,大量意味着常规的二分加排序的方法必然超时,而用自定义排序规则的set和方便存储的map就可以高效解决问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <set>
#include <map>
#include <string>
using namespace std;
struct Word {
int times;
string wd;
};
struct Rule {
bool operator () ( const Word & w1,const Word & w2) const {
if( w1.times != w2.times)
return w1.times > w2.times;
else
return w1.wd < w2.wd;
}
};
int main()
{
string s;
set<Word,Rule> st;
map<string,int> mp;
while( cin >> s )
++ mp[s] ;
for( map<string,int>::iterator i = mp.begin();
i != mp.end(); ++i) {
Word tmp;
tmp.wd = i->first;
tmp.times = i->second;
st.insert(tmp);
}
for(set<Word,Rule>::iterator i = st.begin();
i != st.end(); ++i)
cout << i->wd << " " << i->times << endl;
return 0;
}