【算法笔记】第六章:C++标准模板库(STL)介绍

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sd4567855/article/details/82527824

【算法笔记】第六章:C++标准模板库(STL)介绍

标签(空格分隔):【算法笔记】


第六章:C++标准模板库(STL)介绍

  • 标准模板库(Standard Template Library,STL):封装了很多相当实用的容器。

6.1 vector的常见用法详解

  • vector:翻译为向量,但是使用“变长数组”的称呼可能更准确,也就是“长度根据需要而自动改变的数组”。在很多题目中,我们可以使用vector来避免数组超出内存的情况。另外,vector还可以使用邻接表的方式来存储图,这对于无法使用邻接矩阵的题目,和害怕使用指针实现的邻接表的读者是非常友好的。

  • 如果想要使用vector,需要添加头文件#inlucde<vector>

  • vector的创建:格式vecotr<typename> name;这个定义其实相当于一个一维数组name[size],只不过长度可以依据需求而发生变化,比较节省空间,即变长数组。
    typename可以实任何基本类型,例如int、double、char、结构体、也可以是STL容器,例如vector、set、queue等。要注意的一点:如果typename也可是一个STL容器,定义的时候要在>>符号之间加上空格,因为C++11标准下会把它视为移位操作。
    例如vector< vector< int> > name;
    定义vecotr数组的方法:vector<typedef> Arryname[arraySize];
    二维vecotr数组中,Arryname[]中的每一个元素都是vecotr。这里Arryname[0] ~ Arryname[arraySize - 1]都是vector容器。
    与 vector< vector< int> > name; 不同的是,上述写法的一维长度已经是Arryname, 是一个定值,而这种写法长度可变。

  • vector容器内元素的访问
    两种方式:通过下标访问或者通过迭代器访问。特别说明,二者是等价的.

    1. 通过下标访问:
      和普通数组一样,对于vector< int> name;而言,直接可访问的范围是 0 ~ name.size() - 1,访问这个范围外的元素运行可能会出错。
    2. 通过迭代器访问:
      迭代器(iterator)可以理解为一种类似于指针的东西,定义是:vector<typename>::iterator it;,在这里 it 就是一个vector::iterator型的变量其中typenma就是vector的类型。可以通过 *it 来访问 vector中的元素。另外,迭代器还是实现了自加,自减操作:it++,++it,it–,–it.
      例如:
      vector<int> vi;
      for( int i = 0; i < 5; i++)
          vi.push_back( i);
      vector<int>::iterator it;
      it = vi.begin();
      //begin()函数的作用为取vi的首元素地址
      /*另外注意,end()函数并不是vi尾元素地址,而是取尾元素地址的下一个地址,end()作为迭代起末尾标志,不存储任何元素*/
      //因为美国人习惯左闭右开
      cout<<*(it + 2);
      //其输出结果为 2 .     
      for(vector<int>::iterator it2 = vi.begin() + 3; it2 != vi.end(); it2++)
          cout<<*it2<<' ';
      //输出结果为 3 4.
      //vector迭代起不支持it2 < vi.end()的写法,循环只能使用上述写法。
      //另外,在常用STL中,只有vector 和string支持 "vi.begin() + 3"这样的写法
  • vector常用函数解析
    1. push_back()
      push_back(x)就是在vector后面添加一个元素x,时间复杂度为 O ( 1 ) .
    2. pop_back()
      pop_back()用来删除vector的尾部元素,时间复杂度为 O ( 1 ) .
    3. size()
      size()用来获得vector中元素的个数,时间复杂度为 O ( 1 ) .size()返回的是unsigned类型,不过一般而言用%d不会有很大问题,这一点对于所有的STL容器都是一样的。
    4. clear()
      clear()用来清空vector中所有的元素,时间复杂度为 O ( N ) ,其中N是vector中元素的个数。
    5. insert()
      insert(it,x)用来向vector的任意迭代器it处插入一个元素x,时间复杂度为 O ( N ) .
    6. erase()
      erase()函数有两种用法:删除单个元素、删除一个区间内所有的元素。时间复杂度为 O ( N ) .
      • 删除单个元素:earse(it),删除迭代器为it处的元素.
      • 删除一个区间的所有元素:earse(first,last),删除[first,last)区间内所有元素。如果要删除vector内所有元素,正确的写法应该是earse(a.begin(),a.end())
        例如:
 vector< int> a;
    for(int i = 0;i<=5;i++)
        a.push_back(i);
    a.insert( a.begin() + 2, 10);
    for(int i = 0;i<=5 + 1;i++)
        cout<<a[i]<<' ';
  //输出结果为0 1 10 2 3 4 5
    cout<<endl;
    a.erase( a.begin() + 2);
    for(int i = 0;i<=5 ;i++)
        cout<<a[i]<<' ';
  //输出结果为0 1 10 3 4 5 
    cout<<endl;
    a.erase( a.begin() + 2, a.begin() + 4);
    for(int i = 0;i<=3 ;i++)
        cout<<a[i]<<' ';
  //输出结果为0 1 4 5 
  • vector的常见用途
    1. 存数数据
      vecotr本身可以作为数组使用,而且在一些元素个数不确定的场合可以很好的节省空间。
      同时,有些场合需要根据一些条件把部分数据输出在同一行,数据中间由空格隔开。由于输出的数据的个数是不确定的,为了更方便地处处理最后一个满足条件的数据周免不输出额外的空格,可以先用vector记录所有需要输出的数据,然后一次性输出。
    2. 用邻接表存储图
      使用vector实现邻接表可以让一些对指针不熟悉的读者有一个比较方便的写法。

6.2 set的常见用法详解

  • set:翻译为集合,是一个内部自动有序且不含重复元素的容器。
    对于题目中,需要去掉重复元素的情况,有可能因为某些元素过大或者类型不是int而无法直接开散列表,在这种情况下就可以使用set来保留元素本身而不考虑它的个数。(当然,上述情况也可以再开一个数组来进行下表和元素对应解决)

  • 如果想要使用set,需要添加#include<set>.

  • set的定义:
    格式:set<typename> name;,这个写法和vector基本是一样的(或者说大部分STL都是这样定义的)。这里的typename依然可以是任何基本类型,例如int,double,char,结构体等,或者是STL标准容器,例如vector,set,queue等。:如果typename也是一个STL容器,定义的时候要在>>符号之间加上空格,因为C++11标准下会把它视为移位操作。
    例如:set<int> name;.
    set数组的定义格式set<typename> Arrayname[arraySize];,这样来看Arrayname[0] ~ Arrayname[arraySize - 1]之中每一个都是 set 容器。 例如:set<int> a[100];

  • set容器内元素的访问:set之可以通过迭代器(iter)来访问,迭代器的定义格式set<typename>::iterator it;如此一来,便得到了迭代器it,并且可以通过 *it 来访问set里面的元素。
    再次提醒,除了vector和string之外的STL容器,都不支持*(it+i)的访问方式。 因此只能按照如下方式枚举:

    set<int> st;
    for(set<int>::iterator it = st.begin();it != st.end(); it++){
        printf("%d",*it);
    } 
  • set常用函数实例解析

    1. inset()
      insert(x)可以将x插入到set容器中,并自动递增排序和去重,时间复杂度为 O ( l o g N ) ,其中 N 为set内元素的个数。
    2. find()
      find(value)返回set中对应值为value的迭代器,时间复杂度为 O ( l o g N ) ,其中 N 为set内元素的个数。
    3. earse()
      earse()有两种用法:删除单个元素、删除一个区间内所有元素。
      • 删除单个元素:两种方法,1. st.erase(it) ,其中 it是一个被删除元素的迭代器。时间复杂度为 O ( 1 ) ,可以搭配find()函数使用。st.earse(st.find(x));.2. st.erase(value),其中value为被删除元素的值,时间复杂度为 O ( l o g N ) ,N 为set内元素的个数。
      • 删除一个区间内所有元素:st.erase(first,last),其中first为所需要删除区间的起始迭代器,last指向末尾需要被删除元素的迭代器的下一个地址,即删除[first,last),时间复杂度为 O ( f i r s t l a s t ) .
    4. size()
      size()函数用来获得set内元素的个数,时间复杂度为 O ( 1 ) .
    5. clear()
      clear()函数用来清空set内所有元素,时间复杂度为 O ( N ) ,其中 N 为set内元素的个数。
  • set的常见用途
    set最主要的作用是自动去重并按升序排序,因此碰到需要去重但是不方便直接开数组的问题,可以尝试使用set解决。

  • 延申:set 内元素是唯一的,如果需要处理不唯一的情况,则需要使用multiset,另外,C++11 中还增加了 unordered_set,以散列代替set内部的红黑树(Red Black Tree,一种自平衡二叉查找树)实现,可以用来处理单单去重但是不排序的需求,速度比set快很多。

6.3 string的常见用法详解

  • 在C语言中,一般使用字符数组 char str[]来存放字符串,但是使用字符数组有时候操作十分麻烦,而且会因为经验不足而发生一些错误。C++在STL中引入了string类型,对字符串常用的需求功能进行了封装,方便操作且不易出错。
  • 如果想要使用string函数,需要添加string头文件,即#include<string>

  • string的定义
    格式string后面跟上变量名即可,例如:srtring str;如果需要初始化,可以直接赋值,例如string str="abc123";

  • string 中的内容访问

    1. 通过下标访问:对于string而言,可以直接向字符串数组那样,用下标访问。例如
    2. 如果要写入和读出整个字符串,**只能使用**cin和cout.如果想要使用printf和scanf实现写入和读出,需要首先使用 c_str() 函数将string转化为字符串数组。
      例如:

      string a;
      a.c_str();
      scanf("%s",a);
      printf("%s",a);
    3. 通过迭代器访问:在大多数情况下,通过下标访问即可以满足大部分需求。但是例如insert() 和 erase() 函数要求使用的迭代器为参数。
      string不需要其他的STL容器那样需要参数,因此可以直接定义,格式:string::iterator it;.这样,得到迭代器it之后,便可以通过*it来访问string函数中的每一位。需要注意的是,string 和 vector 一样,支持对迭代器进行加减某个数字,例如str.begin() + 3,这种写法是可行的。

  • string 常用函数实例解析
    string 函数有很多,只介绍集中常用函数。

    1. operator+=
      operator+- 函数用于string的加法,可以将两个字符串直接拼接起来。例如:str1 = str1 + str2;
    2. compare operator
      两个string类型可以直接使用 ==,!=,<=,>=,<,> 比较大小,比较规则是字典序.
    3. length() 和 size()
      length() 和size() 函数返回的是 string 中存放的字符数,时间复杂度为 O ( 1 ) .length() 和 size() 功能基本相同。
    4. insert()
      string 的insert()有很多种写法,这里仅仅给出几个。时间复杂度为 O ( N )

      • insert(pos,string):在pos号位置之前插入字符串string.例如:

        string a ="qwer";
        string b ="asdf";
        b.insert(3,a);
        cout<<b;//输出结果是asdqwerf
      • insert(it,it2,it3):it为原字符串想要插入的位置,迭代器it2和it3为待插入字符串的首尾借贷期,用来表示将[it2,it3)将被插入在it的位置上。例如:

        string a ="qwer";
        string b ="asdf";
        b.insert(b.begin() + 1, a.begin(), a.begin() + 2);
        cout<<b;//输出结果为aqwsdf
    5. erase()
      erase()有两种用法:删除单个元素,删除一个区间内的所有元素。时间复杂度为 O ( l o g N ) .

      • 删除单个元素:str.erase(it),用于删除单个元素,it为被删除元素的迭代器。
      • 删除一个区间内所有元素:1. str.erase(first,last),其中first为被删除区间的起始迭代器,last是被删除元素的迭代器的下个地址,即删除[first,last). 2. str.erase(pos,length),其中pos为需要开始删除的元素的起始位置,length为删除的字符个数。
    6. claer()
      clear()用来清空string中的数据,时间复杂度一般而言为 O ( 1 ) .
    7. substr()
      substr(pos,len),返回从pos号开始、开始为len的字串,时间复杂度为O(len).
    8. string::npos
      string::npos 是一个常数,本身的值为-1,但是由于是unsigned_int 类型,因此实际上可以认为是unsigned_int 类型的最大值。string::npos 可以作为find函数失配时的返回值。
    9. find()
      • str.find(str2),当str2 是str字串时,返回其在str中第一次出现的位置,如果str2不是str字串时,返回str::npos.
      • str.find(str2,pos),从str的pos号位置开始匹配str2,返回值与上相同。
        时间复杂度为 O ( s t r . s i z e ( ) s t r 2. s i z e ( ) ) .
    10. replace()
      • str.replace(pos,len,str2),把str从pos号位置开始,长度为len的字串替换为str2.
      • str.repalce(it1,it2,str2),把str的迭代器[it1,it2)范围的字串替换为str2.时间复杂度为 O ( s t r . l e n g t h ) .

6.4 map的常见用法详解

  • map翻译为映射,是常用的STL容器。map可以将任何基本类型(包括STL容器)映射到任何基本类型(包括STL容器)。在定义数组时(例如 int array[100];),其实是定义了一个从int类型到int类型的映射,例如array[0] = 25;其实是将 0 映射到25. 同理,double型数组则是将int型银蛇到double型,例如db[0] = 3.14;.
    可是无论什么类型,它总是将int型映射到其他类型,这存在一个弊端:当需要以其他类型作为关键字来作为映射时,会十分不方便。使用map便不会出现如此问题。
    另外,由于map内部使用红黑树实现的(和set相同),map会以键从小到大的顺序自动排序,在建立映射的过程中会自动实现从小到大的排序功能。

  • 如果需要使用map,需要添加map的头文件,即#include<map>;

  • map的定义格式:map<typename1, typename2> mp;
    map和其他类型的STL容器的定义有些不同,因为map需要确定映射前类型(键key)和映射后类型(值value),所以在<>内右两个类型,第一个是key的类型,第二个是value的类型。如果是int到int型,便相当于普通的int数组。
    注意,如果是字符串到整型的映射,不能使用char数组 而必须使用string,因为char数组作为数组类型,是不可以被作为键值的。即,如果字符串类型想做映射,必须使用string类型。
    同样,STL容器也可以作为map的键和值。

  • map容器内元素的访问:两种方式,通过下标访问或者通过迭代器访问。

    1. 通过下标访问:和访问普通的数组一样,例如对于定义为 map<char,int> mp;而言,可以直接使用 mp[‘c’] 方式来直接访问它对应的整数。注意:map中的键是唯一的
    2. 通过迭代器访问:map迭代器的定义和其他STL容器迭代器的定义方式相同:map<typename1, typename2>::iterator it;. 需要注意的是,map迭代器的使用方式和其他STL容器的迭代器使用方式不同,因为map的每一对映射都有两个typename,这意味者必须能通过一个it来同时访问键和值。事实上,map可以使用 it->first 来访问键,通过it->second来访问值
  • map常用函数解析

    1. find()
      find(key), 返回键为key的映射的迭代器,时间复杂度为 O ( l o g N ) ,N为map中映射的个数。
    2. erase()
      两种用法: 删除单个元素、删除一个区间内所有元素。
      • 删除单个元素:
        两种方法:1. mp.erase(it),it为被删除元素的迭代器。时间复杂度为 O ( 1 ) . 2. mp.erase(key),其中key为被删除元素的键。时间复杂度为 O ( l o g N ) ,N 为map内元素的个数。
      • 删除一个区间内所有的元素
        mp.erase(first, last),其中first为需要删除的区间的起始迭代器,而last 则为需要删除的区间的末尾迭代器的下一个
        地址。即删除区间为[first,last). 时间复杂度为 O ( l a s t f i r s t )
    3. size()
      size(),用来获得map中映射的对数,时间复杂度为 O ( 1 ) .
    4. clear()
      clear(), 用来清空map中所有元素,时间复杂度为 O ( N ) ,其中N为map中元素的个数。
  • map 的常见用途

    1. 需要建立字符(或者字符串)与整数之间的映射的题目,可以使用map减少代码量。
    2. 判断大整数或者其他类型数据是否存在的题目,可以将 map 当作bool数组使用。
    3. 字符串和字符串的映射可能使用。
  • 拓展: map的键和值是唯一的,如果一个键对应多个值,就只能使用multimap,另外,C++11 中还增加了unordered_map ,以散列代替map内部的红黑树实现,它可以用来处理只映射而不按照key值排序的需求,速度比map快很多。

6.5 queue 的常见用法详解

  • queue 翻译为队列的,在STL中为一个先进先出的容器。
  • queue 的定义:要使用queue ,应该先添加头文件#include<queue>
    格式:queue<typedef> name;,其中 typename 可以是任意基本数据类型 或 容器。
  • queue 容器内元素的访问:在STL中,只可以通过 front() 来访问队首元素,通过 back() 来访问队尾元素。
  • queue 常用函数解析

    1. push()
      push(x) 表示将 x 进行入队,时间复杂度为 O ( 1 ) .
    2. fornt() , back()
      front() 获得队首元素,back()获得队尾元素,二者时间复杂度均为 O ( 1 ) .
    3. pop()
      pop() 令队首元素出队,时间复杂度为 O ( 1 ) .
    4. empty()
      empty() 检测queue是否为空,如果返回true 则队列为空,返回false为非空,时间复杂度为 O ( 1 ) .
      注意:当使用front() 和 pop() 函数之前,必须使用empty() 来检查队列是否为空,否则可能因为队空而出现错误。

    5. size()
      size() 返回queue内元素的格式,时间复杂度为 O ( 1 ) .

  • queue 的常见用途:
    当需要实现广度优先搜索时,可是使用queue。

  • 注意,在STL中,还有两种容器与队列有关,分别是双端队列(deque) 和 优先队列 (priority_queue),前者是首尾均可以插入和删除的队列,后者是使用堆实现的默认将当前最大元素置于队首的容器。

6.6 priority_queue 的常见用法详解

  • priority_queue 又称为优先队列,是用实现的。在 优先队列中,队首元素一定是当前队列中优先级最高的那一个。在任何时候往有优先队列里push元素,而底层heap会随时进行调整,保证 每次的队首元素都是优先级最大的。
  • priority_queue的定义:
    如果想要使用优先队列,首先要添加头文件#include<queue>,优先队列的格式:priority_queue<typename> name;,其中 typename 可以是任意基本数据类型 或 容器。
  • priority_queue 容器内元素的访问
    与 queue 不同的是,优先队列中没有 front() 和 back() 函数,只可以通过top() 函数来访问队首元素(堆顶元素),也就是优先级最高的元素。
  • priority_queue 常用函数实例解析

    1. push()
      push(x) 令x入队,时间复杂度为 O ( l o g N ) ,其中 N 为当前优先队列中元素的个数。
    2. top()
      top() 可以获得队首元素(堆顶元素),时间复杂度为 O ( 1 )
      注意,在使用 top() 函数之前,必须使用 empty() 判断优先队列是否为空,否则可能因为队空而产生错误。
    3. pop()
      pop() 可以令队首元素(堆顶元素)出队,时间复杂度为 O ( l o g N ) ,其中 N 为当前优先队列中元素的个数。
    4. empty()
      empty() 用来检查优先队列是否为空,如果返回 true 则为空,返回 false 则为非空。时间复杂度为 O ( 1 ) 。 5. size()
      size() 返回优先队列中元素的个数,时间复杂度为 O ( 1 )
  • priority_queue 内元素优先级的设置
    下面介绍基本数据类型与结构体类型的优先级设置方法。

    1. 基本数据类型的优先级设置方法
      此处的基本数据类型指的是 int、char、double类型可以直接使用的数据队列,优先队列对于它们的优先级设置一般是 数字大的优先级高 , 因此队首元素是最大的那个(char类型按照字典序最大)。
      对于基本数据类型而言,下面两种优先队列的定义是等价的(以 int 型为例):priority_queue<int> q;
      priority_queue<int , vector<int>, less< int> > q;(注意最后两个 > 之间有空格)可以看出,第二种定义方式的尖括号内多出了两个参数:一个是 vector ,另一个是less.其中 vector 填写的是来承载底层数据结构 heap的容器,如果是double型或者 char型,这里需要填写vector 或者 vector. 第三个参数less 则是对第一个参数的比较类,less 表示数字大的优先级大,而 greater 表示数字小的优先级大
    2. 结构体的优先级设置
      两种方法:使用 overload; 或者构建 cmp函数。
  • priority_queue 的常见用途
    解决一些贪心问题,也可以对Dijkstra算法进行优化。

6.7 stack 的常见用法详解

  • stack 翻译为栈,是STL中实现一个后进先出的容器。
  • stack 的定义:如果要使用stack,应该先添加头文件#include<stack>,stack的格式: stack<typename> name; ,其中 typename 可以是任意基本数据类型或者容器。
  • stack容器内元素的访问
    由于stack本身是一种后进先出的数据结构,因此STL中stack**只可以通过 top() 来访问栈顶元素**。
  • stack常用函数实例解析:
    1. push()
      push(x) 将 x 入栈,时间复杂度为 O ( 1 ) .
    2. top()
      top() 获得栈顶元素,时间复杂度为 O ( 1 ) .
    3. pop()
      pop() 用来弹出栈顶元素,时间复杂度为 O ( 1 ) .
    4. empty()
      empty() 用来检测 stack内是否为空,返回 true 为空,返回 false 为非空,时间复杂度为 O ( 1 ) .
    5. size()
      size() 返回 stack 内元素的个数,时间复杂度为$O(1)
  • stack 常见用途
    stack 用来模拟实现一些递归,防止程序对栈内存的限制而导致程序运行出错。一般而言,程序的栈内存空间很小,对于有些题目而言,如果使用普通的函数进行递归,一旦层数过深,会导致程序崩溃,如果用栈模拟递归算法,就可以避免这一方面的问题。

6.8 pair 的常见用法详解

  • 当不想使用结构体,但有需要把两个元素捆绑在一起视为一个元素时,pair便派上用场。实际上pair可以视为一个内部有两个元素的结构体。

    sturct pair{
        typeName1 first;
        typeName2 second;
    }
    
  • pair的定义:使用pair 应当添加头文件#include<utility>,注意,由于map内部实现设计pair元素,如果添加map头文件, 会自动添加 utility 头文件。
    pair 的格式:pair<typeName1. typeName2> name;,pair有两个参数,分别对应 first 和 second 的数据类型,它们可以是任意基本数据类型或者容器。
    它的初始化,例如:pair<string,int> p("asdf",5);
    如果在代码中想要临时构建一个pair,有以下两种方法:
    1. 将类型定义写在前面,后面用小括号内两个元素的方式:pair<stirng,int>("asdf",5);
    2. 使用自带的make_pair函数:
      make_pair("asdf",5);
  • pair 中元素的访问
    由于pair中只有两个元素,分别是 first和second ,只需要按照结构体的访问方式去访问即可。
  • pair 常用函数实例解析
    1. 比较操作数:
      两个pair 类型数据可以直接使用 == , !=, >, >=, <, <= 比较大小,规则:先比较 first 的大小,只有first 相等才比较 second 的大小
  • pair 的常见用途
    1. 用来代替二元结构体及其构造函数,可以节省编码时间。
    2. 作为map的键值对 来进行插入。例如:mp.insert(make_pair("asdf",5));

6.9 algorithm头文件下的常用函数

6.9.1 max(), min(), abs()

  • 格式:
    1. max(x,y); min(x,y);,参数只能是 2 个,可以是浮点数。
      2.abs(x);. 注意 x 必须是整数。浮点型的绝对值需要使用 math 头文件下的 fabs.

6.9.2 swap()

  • 格式:swap(x,y);用来交换 x和 y 的值。

6.9.3 reverse()

  • 格式:reverse(it1, it2);可以将 数组指针 在[it1,it2)之间的元素 或者 容器的迭代器在[it1,it2)范围内的元素进行反转。

6.9.4 next_permutation()

  • 格式:next_permutation();给出一个序列在全排列中的下一个序列。当 next_permutation 到达全排列中的最后一个时会返回 false,可以用来退出循环。

6.9.5 fill()

  • 格式:fill();.可以把数组或者容器中的某一区间赋为某一个相同的值。和memset不同的是,这里的赋值可以是数组类型对应范围的任意值。

6.9.6 sort()

  • sort函数:它的使用必须加上头文件#include<algorithm>

    1. 格式如下:
      sort(首元素地址(必填), 尾元素地址的下一个地址(必填),比较函数(选填));
      其中,尾元素地址的下一个地址的理解:例如需要排序a[0] ~ a[3] 则为 sort(a,a+4);.
      sort 函数实现的细节不予讨论,但如果需要对序列进行排序,那么序列中的元素一定要有可比性,特别是结构体类型,需要使用compare函数(cmp函数).
    2. cmp函数

      • 基本数据类型数组的排序:
        如果比较函数不填写,那么默认按照从小到大的顺序排序。如果想要从大到小排序,那么就需要使用比较函数cmp来告诉sort何时交换元素。

        bool cmp1( int a, int b){
               return a>b;
        //当a>b时把a放到b前面
        }
      • 结构体数组的排序:
        对于如下结构体:struct node{int x,y;}ssd[10];如果想要ssd数组按照x从大到小的顺序进行排序,那么cmp函数可以这样写:

        bool cmp2( node a, node b){
             return a.x>b.x;
        }
      • 更复杂一点的需求:先按照x从大到小的顺序排序,但是当x相等的情况下,按照y的大小从小到大排序,那么cmp函数的写法为:

        bool cmp2( node a, node b){
        if( a.x != b.x)
            return a.x > b.x;
        else
            return a.y < b.y;
        }
      • 容器的排序:
        在STL标准容器中,只有vector,string,deque是可以使用sort函数的,而其余类似于set、map这样的容器是使用红黑树实现的,元素本身有序。

        bool cmp( int a, int b){
               return a>b;
        }
        
        int main(){
              vector<int> vi;
              // 一系列输入之后....
              sort(vi.begin(), vi.end(), cmp);
        return 0;}

6.9.7 lower_bound() 和 upper_bound()

  • lower_bound() 和 upper_bound() 需要用在一个有序数组或者容器中。
  • 格式:
    1. lower_bound(first,last,val);用寻找数组或者容器的[first,last) 范围内第一个大于等于val的元素的位置。如果是数组,便返回该位置的指针;如果是容器,则返回该位置的迭代器。
    2. upper_bound(first,last,val);用寻找数组或者容器的[first,last) 范围内第一个大于等于val的元素的位置。如果是数组,便返回该位置的指针;如果是容器,则返回该位置的迭代器。
  • 注意:
    1. 如果数组或容器中没有需要寻找的元素,那么lower_bound() 和 upper_bound() 均返回可以插入该元素位置的指针或迭代器.
    2. 如果只是想要查找元素的下标,可以不使用临时指针,直接令返回值减去数组首地址即可
  • lower_bound() 和 upper_bound() 的复杂度均为 O ( l o g ( l a s t f i r s t ) ) .

我的微信公众号

猜你喜欢

转载自blog.csdn.net/sd4567855/article/details/82527824
今日推荐