算法笔记:C++ STL(Standard Template Library)

来源:互联网 发布:java代码流程图 编辑:程序博客网 时间:2024/06/10 21:28

算法笔记:C++ STL

一、排序

1. 对基本类型数组递增排序:sort(数组名+n1, 数组名+n2);  排序区间 [n1, n2)

        这里n1, n2均为大于等于0的整数,若n1=0,可省略不写(下同)

例1:

#include <iostream>#include <algorithm>using namespace std; int main(){int i,len;int a[]={15,4,3,9,7,2,6},b[10],c[10];len=sizeof(a)/sizeof(int);for(i=0;i<len;i++){b[i]=a[i];c[i]=a[i];}sort(a,a+len);    //对a数组所有元素递增排序 cout<<"a:";for(i=0;i<len-1;i++)cout<<a[i]<<" ";cout<<a[len-1]<<endl;sort(b,b+3);      //对b数组第0个到第2个元素递增排序 cout<<"b:";for(i=0;i<len-1;i++)cout<<b[i]<<" ";cout<<b[len-1]<<endl;sort(c+2,c+5);    //对c数组第2个到第4个元素递增排序 cout<<"c:";for(i=0;i<len-1;i++)cout<<c[i]<<" ";cout<<c[len-1]<<endl;return 0;}
2. 对元素类型为T的基本类型数组递减排序:

        sort(数组名+n1, 数组名+n2, greater<T>);   排序区间[n1, n2)

例2:

#include <iostream>#include <algorithm>using namespace std; int main(){int i,len;int a[]={15,4,3,9,7,2,6};len=sizeof(a)/sizeof(int);sort(a,a+len,greater<int>());  //对a数组所有元素递减排序 for(i=0;i<len-1;i++)cout<<a[i]<<" ";cout<<a[len-1]<<endl;return 0;}
3. 用自定义排序规则,对任何类型T的数组排序:

        sort(数组名+n1, 数组名+n2, 排序规则结构名());

例3:学生信息排序

        输入n个学生的信息(1<=n<=1000),包括学生学号、姓名与成绩,按学生成绩递增排序;若学生成绩相同,按姓名字典序递增排序;若学生成绩与姓名均相同, 按学号递增排序

#include <iostream>#include <algorithm>#define maxn 1010using namespace std;struct Student{int no;         //学号char name[32];  //姓名double score;   //成绩 } stu[maxn];struct Rule{bool operator()(struct Student s1,struct Student s2){if(s1.score==s2.score){if(strcmp(s1.name,s2.name)==0)return (s1.no<s2.no);elsereturn (strcmp(s1.name,s2.name)<0);}elsereturn (s1.score<s2.score);}};int main(){int i,n;cin>>n;for(i=0;i<n;i++)cin>>stu[i].no>>stu[i].name>>stu[i].score;sort(stu,stu+n,Rule());for(i=0;i<n;i++)cout<<stu[i].no<<" "<<stu[i].name<<" "<<stu[i].score<<endl;return 0;}
二、二分查找:在排好序(递增/递减)的数组上进行查找操作,以下以按元素值递增预排序为例

1. binary_search

(1)在排好序的基本类型数组上二分查找:binary_search(数组名+n1, 数组名+n2, 值);  

        ①查找区间 [n1, n2),即在该区间内找等于"值"的元素,若找到返回true,否则返回false(这里的"等于"不是==,而是“a<b 和 b<a”都不成立)

        ②查找与排序的规则必须一致,否则查找无意义

(2)在用自定义排序规则排好序的、元素为任意的T类型的数组中二分查找:binary_search(数组名+n1, 数组名+n2, 值, 排序规则结构名());  查找区间 [n1, n2)

        ①查找方法同上,且查找与排序的规则必须一致

        ②注意"等于"不是==,而是"a必须排在b前面 和 b必须排在a前面"都不成立

例4:

#include <iostream>#include <algorithm>#define maxn 1010using namespace std;struct Rule       //自定义排序规则:按个位数递增排序 {bool operator()(const int &a1,const int &a2){return (a1%10 < a2%10);}};int main(){int a[]={12,45,3,98,21,7};sort(a,a+6);  //预排序cout<<binary_search(a,a+6,12)<<endl;  //找到,返回1 cout<<binary_search(a,a+6,77)<<endl;  //未找到,返回0sort(a,a+6,Rule());  //按自定义排序规则排序cout<<binary_search(a,a+6,7)<<endl;   //找元素7,注意这里元素已经不是按元素值递增排序的,因此查找无意义,返回0 cout<<binary_search(a,a+6,8,Rule())<<endl;   //按自定义排序规则能找到个位数为8的元素,返回1 return 0;}

2. lower_bound:二分查找下界

(1)在对元素类型为T的排好序的基本类型数组中查找:T* lower_bound(数组名+n1, 数组名+n2, 值);

        返回一个指针T *p,这里*p是查找区间里下标最小的、大于等于"值"的元素。若找不到,p指向下标为n2的元素

(2)在对元素类型为任意的T类型、按自定义排序规则排好序的数组中查找:T* lower_bound(数组名+n1, 数组名+n2, 值, 排序规则结构名());

        返回一个指针T *p,这里*p是查找区间里下标最小的、按自定义排序规则可排在"值"后面的元素。若找不到,p指向下标为n2的元素

3. upper_bound:二分查找上界

(1)在对元素类型为T的排好序的基本类型数组中查找:T* lower_bound(数组名+n1, 数组名+n2, 值);

        返回一个指针T *p,这里*p是查找区间里下标最小的、大于"值"的元素。若找不到,p指向下标为n2的元素

(2)在对元素类型为任意的T类型、按自定义排序规则排好序的数组中查找:T* lower_bound(数组名+n1, 数组名+n2, 值, 排序规则结构名());

        返回一个指针T *p,这里*p是查找区间里下标最小的、按自定义排序规则必须排在"值"后面的元素。若找不到,p指向下标为n2的元素

例5:

#include <iostream>#include <algorithm>#define maxn 1010using namespace std;struct Rule       //自定义排序规则:按个位数递增排序 {bool operator()(const int &a1,const int &a2){return (a1%10 < a2%10);}};int main(){int a[]={12,5,3,5,98,21,7};sort(a,a+7);  //预排序int *p=lower_bound(a,a+7,5);//查找5的下界cout<<*p<<" "<<p-a<<endl;   //输出下界元素值及下标(5,1) p=upper_bound(a,a+7,5);     //查找5的上界cout<<*p<<" "<<p-a<<endl;   //输出上界元素值及下标(7,3) cout<<*upper_bound(a,a+7,13)<<endl;  //输出13的上界(21) sort(a,a+7,Rule());     //按个位数递增排序cout<<*lower_bound(a,a+7,16,Rule())<<endl; //查找16的下界(7)cout<<*upper_bound(a,a+7,5,Rule())<<endl;  //查找5的上界(7)cout<<*upper_bound(a,a+7,4,Rule())<<endl;  //查找4的上界(5)cout<<lower_bound(a,a+7,25,Rule())-a<<endl;  //查找25的下界的下标(3)cout<<upper_bound(a,a+7,18,Rule())-a<<endl;  //查找18的上界的下标(7,表示未找到)if(upper_bound(a,a+7,18,Rule())==a+7)      //未找到18的上界 cout<<"Not found"<<endl; return 0;}

三、STL中平衡二叉树数据结构

        引入:有时需要在大量增加删除数据的同时,还要进行大量数据的查找操作,希望增、删、查都能在O(logn)复杂度内完成。(注意排序+二分查找显然不可以,因加入新数据就要对数据重新排序)因此可使用"平衡二叉树"数据结构存放数据,体现在STL中即4中排序容器:multiset、set、multimap、map

1. multiset(需包含<set>头文件)

(1)定义:multiset<T> st;

        定义了一个multiset变量st,st内可存放T类型数据,且可以自动排序以随时保持st中的数据递增有序。初始状态st为空。

        排序规则:表达式"a<b"为true,则a排在b前面。

(2)multiset上的迭代器:multiset<T>:: iterator p;

        这里p是迭代器,近似指针,可用于指向multiset中的元素。访问multiset中的元素要通过迭代器。

        注意迭代器与指针的不同:multiset上的迭代器可进行++、--、用==和!=比较,但不可以比大小、加减整数、相减。

(3)常用操作:

        ①st.begin() 返回值类型为multiset<T>::iterator,即指向st中头一元素的迭代器

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

        ③st.insert(x) 将元素x插入迭代器,实际插入的是x的复制品

        ④st.find(x) 查找元素x,返回一个迭代器

        ⑤st.erase(x) 删除元素x。若有多个元素值为x,则均删除

        ⑥对迭代器++,其就指向容器中的下一元素;--则指向容器中的上一元素

        ⑦迭代器遍历元素:通过循环实现

        ⑧multiset中的二分查找操作:

        1)lower_bound(x)  返回最靠后的迭代器it,使得[begin(),it)中的元素都在x前面

        2)upper_bound(x)  返回最靠前的迭代器it,使得[it,end())中的元素都在x后面

(4)自定义排序规则的multiset

        例如:multiset<int, greater<int> > st;      将st中的元素从大到小排序

                    multiset<int, Rule> st2;                  将st中的元素按Rule规则排序

例6:

#include <iostream>#include <set>using namespace std;int main(){int i;int a[10]={1,14,12,13,7,13,21,19,8,8};multiset<int> st;     //定义一个名为st的multiset for(i=0;i<10;i++)     //向st中插入a[i]的复制品,并保持有序 st.insert(a[i]);multiset<int>::iterator p;  //迭代器p,近似指针for(p=st.begin();p!=st.end();p++) //遍历st中的元素并输出 cout<<*p<<" ";cout<<endl;p=st.lower_bound(13); //返回最靠后的迭代器it,使得[begin(),it)中的元素都在13前面 cout<<*p<<endl;p=st.upper_bound(8);  //返回最靠前的迭代器it,使得[it,end())中的元素都在8后面 cout<<*p<<endl;st.erase(p);     //删除迭代器p指向的元素(12) for(p=st.begin();p!=st.end();p++) //遍历st中的元素并输出 cout<<*p<<" ";cout<<endl;p=st.find(22);   //在st中查找22,返回值是迭代器 if(p==st.end())  //找不到(此时p指向st中最后一个元素的后面) cout<<"Not found"<<endl;elsecout<<"Found:"<<*p<<endl;st.insert(22);   //将22插入到st中,并保持有序 p=st.find(22);   //在st中查找22,返回值是迭代器 if(p==st.end())  //找不到 cout<<"Not found"<<endl;else             //否则找到,输出p指向的元素 cout<<"Found:"<<*p<<endl;return 0;}

例7:结合自定义规则对学生成绩排序

#include <iostream>#include <cstring>#include <set>#include <algorithm>#define maxn 1010using namespace std;int n;struct Student{char name[20];int id;int score;} students[maxn];struct Rule     //自定义排序规则:成绩递减排序,若成绩相同再按姓名字典序递增排序 {bool operator()(const Student &s1,const Student &s2){if(s1.score!=s2.score)return (s1.score>s2.score);elsereturn (strcmp(s1.name,s2.name)<0);}};int main(){int i;multiset<Student,Rule> st;multiset<Student,Rule>::iterator s;cin>>n;for(i=0;i<n;i++){cin>>students[i].name>>students[i].id>>students[i].score;st.insert(students[i]);}for(s=st.begin();s!=st.end();s++)cout<<s->name<<" "<<s->id<<" "<<s->score<<endl;return 0;}
2. set(需包含<set>头文件)

        用法与multiset基本相同,但需注意以下区别:

        (1)set中不能有重复元素(以"a和b重复"为例,意思是"a必须排在b前面"和"b必须排在a前面"均不成立)

        (2)插入元素可能不成功(针对待插入元素在set中已存在的情况)

※拓展:pair模板的使用方法

        pair<T1,T2> 等价于一个结构体:

        其中变量名first与second固定,T1、T2为变量类型(可使用基本类型或自定义类型)

struct{T1 first;T2 second; }
例8:set实现数组去重排序

#include <iostream>#include <cstdio>#include <set>using namespace std;int n;int a[10005];int main(){int i,first=1;set<int> s;set<int>::iterator it;scanf("%d",&n);for(i=0;i<n;i++){scanf("%d",&a[i]);s.insert(a[i]);}for(it=s.begin();it!=s.end();it++){if(first){cout<<*it;first=0;}elsecout<<" "<<*it;}cout<<endl;return 0;}
3. multimap(需包含<map>头文件)

(1) 定义:multimap<T1, T2> mp;

        ①定义了一个multimap变量mp,其容器内的元素都是pair形式的(等价于上文结构体定义)

        ②multimap中的元素按first的值排序,并可以按first进行查找

        缺省排序规则:"a.first<b.first"为true,则a排在b前面。

(2)multimap上的迭代器:multiset<T1, T2>:: iterator p;

        这里p是迭代器,近似指针,可用于指向multimap中的元素。访问multimap中的元素要通过迭代器。

(3)常用操作:类似multiset与set中的基本操作

begin() 返回指向map头部的迭代器
clear() 删除所有元素
count(elem) 返回指定元素出现的次数
empty() 如果map为空则返回true
end() 返回指向map末尾的迭代器
equal_range() 返回特殊条目的迭代器对
erase() 删除一个元素
find() 查找一个元素
get_allocator() 返回map的配置器
insert() 插入元素
key_comp() 返回比较元素key的函数
lower_bound() 返回键值>=给定元素的第一个位置
max_size() 返回可以容纳的最大元素个数
rbegin() 返回一个指向map尾部的逆向迭代器
rend() 返回一个指向map头部的逆向迭代器
size() 返回map中元素的个数
swap() 交换两个map
upper_bound() 返回键值>给定元素的第一个位置
value_comp() 返回比较元素value的函数

        这里需要注意的是,基本操作以pair为单位。

        以插入操作为例,multimap<string, int> MP;  MP mp;  mp.insert(make_pair(a, b));  其中a为字符串,b为一个整数。

例9:学生成绩录入和查询系统模拟


样例输入:

ADD 12 Jack 78

Query 78

Query 81

Add 9 Percy 81

Add 8 Mary 81

Query 82

Add 11 Tom 79

Query 80

Query 81

样例输出:

Nobody

12 Jack 78

9 Percy 81

11 Tom 79

11 Tom 79

#include <iostream>#include <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;  //score+info的组合 int main(){MAP_STD mp;Student st;int sc; char cmd[20];   //Add/Query命令while(cin>>cmd){if(cmd[0]=='A')   //表示命令是Add:输入学生信息并插入mp {   //make_pair生成一个pair<int,StudentInfo>变量,其first=st.score second=st.info cin>>st.info.id>>st.info.name>>st.score;mp.insert(make_pair(st.score,st.info));}else if(cmd[0]=='Q'){cin>>sc;MAP_STD::iterator p=mp.lower_bound(sc);if(p!=mp.begin()){--p;sc=p->first;   //比要查询分数低的最高分MAP_STD::iterator maxp=p;int maxId=p->second.id; for(;p!=mp.begin() && p->first==sc;--p) //遍历所有成绩和score相等的学生 {if(p->second.id>maxId)  //若发现学号更大的学生,则更新 {maxp=p;maxId=p->second.id;}}if(p->first==sc)  //如果上面循环是因为p==mp.begin()而终止,则p指向的元素还要处理{if(p->second.id>maxId)  //若发现学号更大的学生,则更新 {maxp=p;maxId=p->second.id;}} cout<<maxp->second.id<<" "<<maxp->second.name<<" "<<maxp->first<<endl;}elsecout<<"Nobody"<<endl;}} return 0;}

4. map(需包含<map>头文件)

        用法与multimap基本相同,但需注意以下区别:

        (1)map中不能有重复元素(以"a和b重复"为例,意思是"a必须排在b前面"和"b必须排在a前面"均不成立)

        (2)可使用[ ],下标为关键字,返回值为(first和关键字相同的元素)second。即修改某个pair的first对应的second元素的值。

        (3)插入元素可能不成功(针对待插入元素在map中已存在的情况)

        由上述(2)可知,map也称为“关联数组”,实为从键(key)到值(value)的映射。

        例如可以用一个map<string, int> month_name 来表示“月份名字到月份编号”的映射,然后用month_name["July"] = 7 这样的方式来赋值

例10:

#include <iostream>#include <string>#include <map>using namespace std;struct Student{string name;int score;} student[5]={{"Jack",89},{"Tom",74},{"Cindy",87},{"Alysa",87},{"Michael",98}};typedef map<string,int> MP;int main(){MP mp;for(int i=0;i<5;i++)mp.insert(make_pair(student[i].name,student[i].score));cout<<mp["Jack"]<<endl;   //输出first和关键字"Jack"相同的元素的second值(89)mp["Jack"]=60;    //相当于修改Jack的成绩为60for(MP::iterator i=mp.begin();i!=mp.end();i++)   //插入,同时按姓名字典序递增排序 cout<<"("<<i->first<<","<<i->second<<")"<<endl;Student st;st.name="Jack";st.score=99;pair<MP::iterator,bool> p=mp.insert(make_pair(st.name,st.score));  //试探插入st的复制品 if(p.second)      //p.second为true的情况,表示插入成功 cout<<"("<<p.first->first<<","<<p.first->second<<")"<<endl;  //迭代器指向刚刚插入到mp中的元素elsecout<<"insertion failed"<<endl;mp["Harry"]=78;   //相当于插入学生"Harry",成绩为78MP::iterator q=mp.find("Harry");cout<<"("<<q->first<<","<<q->second<<")"<<endl; return 0;}
例11:单词词频统计程序(补充条件:输入以"0"结束)

#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){if(w1.times!=w2.times)return (w1.times>w2.times);elsereturn (w1.wd<w2.wd);}}; int main(){string s;set<Word,Rule> st;map<string,int> mp;while(cin>>s)    //不停地读入单词{if(s=="0")break; ++mp[s];     //找mp中的元素,first=s,返回second,相当于++second(增加s的出现次数1次);若找不到,插入并增加出现次数1次 }for(map<string,int>::iterator i=mp.begin();i!=mp.end();i++)   //将所有单词的统计结果插入st中{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;}

原创粉丝点击