STL函数

C++学习笔记(六)

Posted by Chen Jinhao on August 18, 2021

STL函数(Mooc笔记)

1.排序算法sort

(1)对基本类型的数组从小到大排序:

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

n1和n2都是int类型的表达式,可以包含变量;

将数组中下标范围为[n1,n2)的元素从小到大排序(下标为n2的元素不在排序区间内);

(2)对元素类型为T的基本类型数组从大到小排序:

**sort(数组名+n1,数组名+n2,greater());**

(3)用自定义的排序规则,对任何类型T的数组排序

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

排序规则结构的定义方式格式如下:

struct name
{
	bool operator()(const T &a1,const T &a2)const{
	//若a1应该在a2前面,则返回true
	//否则返回false
	}
}

例如

struct Rule1//按从大到小排序
{
    bool operator()(const int &a1,const int &a2)const
    {
        return a1>a2;
	}
};
struct Rule2//按个位数从小到大排序
{
    bool operator()(const int &a1,const int &a2)
    {
        return a1%10<a2%10;
	}
}

实例

/*用sort对结构数组进行排序*/
#include <bits/stdc++.h>
using namespace std;
struct Student
{
    char name[20];
    int id;
    double gpa;
};
Student students[]={
    {"Jack",112,3.4},{"Mary",102,3.8},{"Mary",117,3.9},
    {"Ala",333,3.5},{"Zero",101,4.0}};
struct StudentRule1{//按姓名从小到大排
    bool operator()(const Student &s1,const Student &s2)const{
        if(stricmp(s1.name,s2.name)<0)//stricmp函数表示不区分大小写的字符串比较
        return true;
        return false;
    }
};
struct StudentRule2
{ //按id从小到大排
    bool operator()(const Student &s1, const Student &s2) const
    {
        return s1.id<s2.id;
    }
};
struct StudentRule3
{ //按gpa从高到低排
    bool operator()(const Student &s1, const Student &s2) const
    {
        return s1.gpa > s2.gpa;
    }
};
void PrintStudents(Student s[],int size)
{
    for(int i=0;i<size;i++)
    {
        cout<<"("<<s[i].name<<","<<s[i].id<<","<<s[i].gpa<<")";
    }
    cout<<endl;
}
int main()
{
    int n=sizeof(students)/sizeof(Student);
    sort(students,students+n,StudentRule1());
    PrintStudents(students,n);
    sort(students, students + n, StudentRule2());
    PrintStudents(students, n);
    sort(students, students + n, StudentRule3());
    PrintStudents(students, n);
    return 0;
}

2.二分查找算法

STL提供在排好序的数组上进行二分查找的算法

  • binary_search
  • lower_bound
  • upper_bound

(1).用binary_search进行二分查找(用法一)

binary_search(数组名+n1,数组名+n2,值);

在已经排好序的数组中查找区间为下标范围为[n1,n2)的元素,返回值为true(找到)或false(未找到)

在这里“等于”的含义是:a<b和b<a都不成立

(2).用binary_search进行二分查找(用法二)

在用自定义排序规则排好序的、元素为任意的T类型的数组中进行二分查找

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

查找区间同(1)

在这里“等于”的含义是:“a必须在b前面”和“b必须在a前面”都不成立

(3).用lower_bound二分查找下界(用法一)

在对元素类型为T的从小到大排好序的基本类型的数组中进行查找

T * lower_bound(数组名+n1,数组名+n2,值);

返回一个指针 T * p;

*p是查找区间里下标最小的,大于等于“值”的元素。如果找不到,p指向下标为n2的元素

(4).用lower_bound二分查找下界(用法二)

在元素为任意的T类型、按照自定义排序规则的数组中进行查找

T * lower_bound(数组名+n1,数组名+n2,值,排序规则结构名());

返回一个指针T * p;

*p是查找区间里下标最小的,按自定义规则排序,可以排在“值”后面的元素。如果找不到,p指向下标为n2的元素

(5).用upper_bound二分查找上界 两种 用法类似lower_bound:略

返回一个指针 T* p;

*p是查找区间里下标最小的,大于“值”的元素(必须排在“值”后面的数)。如果找不到,p指向下标为n2的元素

3.STL里的平衡二叉树结构

(1).multiset用法

multiset st;

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

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

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

用法示例:

#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,j; //迭代器,近似于指针
    for (i = st.begin(); i != st.end(); ++i)
        cout << *i << ",";
    cout << endl;
    i = st.find(22);
    if (i == st.end())
        cout << "not found" << endl;
    st.insert(22);
    i = st.find(22);
    if (i == st.end())
        cout << "not found" << endl;
    else
        cout << "found:" << *i << endl;
    i = st.lower_bound(13);
    cout << *i << endl;
    i = st.upper_bound(8);
    cout << *i << endl;
    j=st.erase(i);
    //erase函数参数应该是一个指向需要被删除的元素的迭代器,返回值为删除过后紧随其后的元素对应的迭代器
    cout<<*j<<endl;
    for (i = st.begin(); i != st.end(); i++)
        cout << *i << ",";
    return 0;
}

multiset ::iterator p;

  • p是迭代器,相当于指针,可用于指向multiset中的元素。访问multiset中的元素要经过迭代器
  • 与指针不同的地方在于:multiset上的迭代器可++,–,用!=和==比较,不可比大小,不可加减整数,不可相减

(2).自定义排序规则的multiset用法

#include <iostream>
#include <cstring>
#include <set>
using namespace std;
struct Rule1{
    bool operator()(const int & a,const int &b){
        return (a%10)<(b%10);
    }//返回值为true说明a必须排在b前面
};
int main()
{
    multiset<int,greater<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]);
    multiset<int,greater<int>>::iterator i;
    for(i=st.begin();i!=st.end();i++)
        cout<<*i<<",";
    cout<<endl; 
    multiset<int,Rule1> st2;
    for(int i=0;i<10;i++)
    {
        st2.insert(a[i]);
    }
    multiset<int,Rule1>::iterator p;
    for(p=st2.begin();p!=st2.end();p++)
        cout<<*p<<",";
    cout<<endl;
    p=st2.find(133);//find(x)的意义是找到一个y,使得“y必须在x前面”和“x必须在y前面”都不成立
    cout<<*p<<endl;
    return 0;
}

(3).set的用法

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

    a和b重复:“a必须排在b前面”和“b必须排在a前面”都不成立

  • set插入元素可能不成功

#include <iostream>
#include <cstring>
#include <set> 
using namespace std;
int main()
{
    set<int> st;
    int a[10] = {1,2,3,8,7,7,5,6,8,12};
    for (int i = 0; i < 10; i++)
        st.insert(a[i]);
    cout<<st.size()<<endl;//输出:8
    set<int>::iterator i;
    for (i = st.begin(); i != st.end(); ++i)
        cout << *i << ",";//输出:1,2,3,5,6,7,8,12
    cout << endl;
    pair<set<int>::iterator,bool> result=st.insert(2);
    //这里的pair相当于一个结构体,包含了一个迭代器和一个bool表示是否插入成功
    if(!result.second)//条件不成立说明插入不成功
        cout<<*result.first<<"already exists."<<endl;
    else
        cout<<*result.first<<"inserted."<<endl;
    return 0;
}

pair模板的用法实例

pair<int,double> a;

等价于:

struct{
	int first;
	double second;
};
a.first=1;
a.second=93.93;

(4).multimap的用法

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

multimap<T1,T2>mp;

则mp里的元素都是以下类型:

struct {
	T1 first;//关键字
	T2 second;//值
}

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

缺省的排序规则是“a.first<b.first”为true,则a排在b前面

应用实例:找出比设定分数低的所有学生中分数最高的,并输出其名字和Id

#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;
//此后MAP_STD等价于multimap<int,StudentInfo>
typedef int* PINT;
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相等的学生,找出Id最大的学生
                    if(p->second.id>maxId)
                    {
                        maxp=p;
                        maxId=p->second.id; 
                    }
                }
                if(p->first==score)//如果上一个for循环的结束是由于p==mp.degin()而终止且p->first恰好也等于score则需要补充判断 
                {
                    if (p->second.id > maxId)
                    {
                        maxp = p;
                        maxId = p->second.id;
                    }
                }
                cout<<maxp->second.name<<" "
                <<maxp->second.id<<" "
                <<maxp->first<<endl;
            }
            else cout<<"Nobody"<<endl;//lower_bound返回begin,说明没有比查询分数低的学生
        }
    }
    return 0;
}

(5).map的用法

于multimap的区别在于:

  • 不能有关键字重复的元素
  • 可以使用[],下标为关键字,返回值为first和关键字相同的元素的second
  • 插入元素可能失败

应用实例1:

#include <iostream>
#include <map>
#include <string>
using namespace std;
struct Student{
    string name;
    int score;
};
Student student[5]={
{"Jack",89},{"Tom",74},{"Cindy",87},
{"Alysa",87},{"Micheal",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;//输出89
    mp["Jack"]=60;//修改名为“Jack”的元素的second
    for(MP::iterator i=mp.begin();i!=mp.end();i++)
        cout<<"("<<i->first<<","<<i->second<<")";
    cout<<endl;
    Student st;
    st.name="Jack";
    st.score=99;
    pair<MP::iterator,bool> p=mp.insert(make_pair(st.name,st.score));
    if(p.second)
        cout << "(" << p.first->first << "," << p.first->second << ")";
    else
        cout<<"insertion failed"<<endl;
    mp["Harry"]=78;//插入一元素,其first为"Harry",然后将其second改为78
    MP::iterator q=mp.find("Harry");
    cout << "(" << q->first << "," << q->second << ")" << endl;
    //输出(Harry,78)
    return 0;
}

应用实例2:统计词频

#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;
        else return w1.wd<w2.wd;
    }
};
int main()
{
    string s;
    set<Word,Rule> st;//set用于统计每个单词的出现次数,且排序规则是词频
    map<string,int> mp;//map用于查找单词本身,所以排序顺序是按照字典序
    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;
}