『算法-ACM竞赛-疯子的算法总结』2 STL Ⅰ 算法 ( algorithm )

『算法-ACM 竞赛-疯子的算法总结』2 STL Ⅰ 算法 ( algorithm )

写在前面: 为了能够使后续的代码具有高效简洁的特点,在这里讲一下 STL,就不用自己写堆,写队列,但是做为 ACMer 不用学的很全面,我认为够用就好,我只写我用的比较多的。

什么是 STL(STl 内容):

容器(Container):
是一种数据结构,如 list,vector,和 deques ,以模板类的方法提供。为了访问容器中的数据,可以使用由容器类输出的迭代器;
迭代器(Iterator):
提供了访问容器中对象的方法。例如,可以使用一对迭代器指定 list 或 vector 中的一定范围的对象。迭代器就如同一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器也可以是那些定了 operator*()以及其他类似于指针的操作符地方法的类对象;
算法(Algorithm):
是用来操作容器中的数据的模板函数。例如,STL 用 sort()来对一个 vector 中的数据进行排序,用 find()来搜索一个 list 中的对象,函数本身与他们操作的数据的结构和类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用;
仿函数(Functor)
适配器(Adaptor)
分配器(allocator)

仿函数、适配器、与分配器用的比较少,甚至没用过!在这里不做说明,有兴趣可以自己学习一下,那个东西 C++软件工程可能用的比较多。

一、算法 ( algorithm )

如果有不理解的容器知识可以先去看看容器

<一>查找算法(9 个):判断容器中是否包含某个值

(可以去看看 C++primer 学学别的,但是我认为太多了没必要)
1.count:
利用等于操作符,把标志范围内的元素与输入值比较,返回相等元素个数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int a[14]={0,1,2,3,4,5,6,7,7,7,7,7,7,8};
cout<<count(a,a+14,7)<<endl;
vector<int> demo;
for(int i=0;i<10;i++)
demo.push_back(i);
demo.push_back(1);
cout<<count(demo.begin(),demo.end(),1)<<endl;
}
//运行结果 6 2;

2.count_if:
利用输入的操作符,对标志范围内的元素进行操作,返回结果为 true 的个数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a)
{
return (a>1);
}
int main()
{
int a[14]={0,1,2,3,4,5,6,7,7,7,7,7,7,8};
int po=count_if(a,a+14,cmp);
cout<<po<<endl;
vector<int> demo;
for(int i=0;i<10;i++)
demo.push_back(i);
demo.push_back(1);
int poi=count_if(demo.begin(),demo.end(),cmp);
cout<<poi<<endl;
}// 运行结果 8 12
//看到网上大佬的代码写的比较深奥,特地去查了查书,我这样用没毛病的。

补充:捕获值列表,是允许我们在 Lambda 表达式的函数体中直接使用这些值,捕获值列表能捕获的值是所有在此作用域可以访问的值,包括这个作用域里面的临时变量,类的可访问成员,全局变量。捕获值的方式分两种,一种是按值捕获,一种是按引用捕获。顾名思义,按值捕获是不改变原有变量的值,按引用捕获是可以在 Lambda 表达式中改变原有变量的值。

2、=。函数体内可以使用 Lambda 所在作用范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
3、&。函数体内可以使用 Lambda 所在作用范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
4、this。函数体内可以使用 Lambda 所在类中的成员变量。
5、a。将 a 按值进行传递。按值进行传递时,函数体内不能修改传递进来的 a 的拷贝,因为默认情况下函数是 const 的。要修改传递进来的 a 的拷贝,可以添加 mutable 修饰符。
6、&a。将 a 按引用进行传递。
7、a, &b。将 a 按值进行传递,b 按引用进行传递。
8、=,&a,&b。除 a 和 b 按引用进行传递外,其他参数都按值进行传递。
9、&, a, b。除 a 和 b 按值进行传递外,其他参数都按引用进行传递。

3.equal_range:
功能类似 equal,返回一对 iterator,第一个表示 lower_bound,第二个表示 upper_bound。

#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a)
{
    return (a>1);
}
int main()
{
 //   int a[14]= {0,1,2,3,4,5,6,7,7,7,7,7,7,8};
    //equal_range(a,a+14,auto po);
    vector<int> demo;
    for(int i=0; i<10; i++)  demo.push_back(i);
    demo.push_back(1);
   cout<<*equal_range(demo.begin(),demo.end(),7).first<<endl;
   cout<<*equal_range(demo.begin(),demo.end(),7).second<<endl;
   cout<<equal_range(demo.begin(),demo.end(),7).first-demo.begin()<<endl;
   cout<<equal_range(demo.begin(),demo.end(),7).second-equal_range(demo.begin(),demo.end(),7).first<<endl;
}
//也可以加cmp函数,同样适用于数组,在下文中不再举出数组的例子

4.find:
利用底层元素的等于操作符,对指定范围内的元素与输入值进行比较。当匹配时,结束搜索,返回该元素的一个 InputIterator。

补充
InputIterator 是用于输入的 Iterator
OutputIterator 是用于输出的 Iterator
ForwardIterator 是 InputIterator,同时可以保证++运算不会使之失效
RandomIterator 是 ForwardIterator,同时具有+,-,+=,-=等运算及各种比较操作

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int a[14]= {0,1,2,3,4,5,6,7,7,7,7,7,7,8};
vector<int> demo;
for(int i=0; i<14; i++) demo.push_back(a[i]);
demo.push_back(1);
cout<<find(demo.begin(),demo.end(),8)-demo.begin()<<endl;
} //可以直接取地址获取值。

5.find_end:
在指定范围内查找”由输入的另外一对 iterator 标志的第二个序列”的最后一次出现。找到则返回最后一对的第一个 ForwardIterator,否则返回输入的”另外一对”的第一个 ForwardIterator。重载版本使用用户输入的操作符代替等于操作。

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int a[14]= {0,1,2,3,4,5,6,7,7,7,7,7,7,8};
vector<int> demo;
for(int i=0; i<14; i++) demo.push_back(a[i]);
demo.push_back(1);
cout<<find_end(demo.begin(),demo.end(),a+2,a+3)-demo.begin()<<endl;
}

6.find_first_of:
在指定范围内查找”由输入的另外一对 iterator 标志的第二个序列”中任意一个元素的第一次出现。重载版本中使用了用户自定义操作符。

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int a[14]= {0,1,2,3,4,5,6,7,7,7,7,7,7,8};
vector<int> demo;
for(int i=0; i<14; i++) demo.push_back(a[i]);
demo.push_back(1);
cout<<find_first_of(demo.begin(),demo.end(),a+2,a+3)-demo.begin()<<endl;
}

7.find_if:
使用输入的函数代替等于操作符执行 find。返回的是迭代器,为了是大家更明白的理解,减去第一个元素的位置,就相当于得到了下标;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int w) {
return w>5;
}
int main()
{
int a[14]= {0,1,2,3,4,5,6,7,7,7,7,7,7,8};
vector<int> demo;
for(int i=0; i<14; i++) demo.push_back(a[i]);
cout<< find_if(demo.begin(),demo.end(),cmp)-demo.begin();
}

8.lower_bound:
返回一个 ForwardIterator,指向在有序序列范围内的可以插入指定值而不破坏容器顺序的第一个位置。重载函 数使用自定义比较操作。
在一个有序的范围内时间复杂度为 log2n,普遍适用于二分算法。
跟 3.equal_range 的用法一样不过这个返回的是 first
9.upper_bound:
返回一个 ForwardIterator,指向在有序序列范围内插入 value 而不破坏容器顺序的最后一个位置,该位置标志 一个大于 value 的值。重载函数使用自定义比较操作。跟 3.equal_range 的用法一样不过这个返回的是 second;

<二>排序和通用算法(7 个):提供元素排序策略
  1. inplace_merge:

    合并两个有序序列,结果序列覆盖两端范围。重载版本使用输入的操作进行排序。

  2. merge:

    合并两个有序序列,存放到另一个序列。重载版本使用自定义的比较。 nth_element:
    将范围内的序列重新排序,使所有小于第 n 个元素的元素都出现在它前面,而大于它的都出现在后面。重载版本使用自定义的比较操作。

  3. partial_sort:

    对序列做部分排序,被排序元素个数正好可以被放到范围内。重载版本使用自定义的比较操作。

  4. partial_sort_copy:

    与 partial_sort 类似,不过将经过排序的序列复制到另一个容器。 partition:
    对指定范围内元素重新排序,使用输入的函数,把结果为 true 的元素放在结果为 false 的元素之前。 random_shuffle:
    对指定范围内的元素随机调整次序。重载版本输入一个随机数产生操作。 reverse:
    将指定范围内元素重新反序排序。 reverse_copy: 与 reverse 类似,不过将结果写入另一个容器。

  5. rotate:

将指定范围内元素移到容器末尾,由 middle 指向的元素成为容器第一个元素。

  1. rotate_copy:

    与 rotate 类似,不过将结果写入另一个容器。

  2. sort:(常用,相信大家都不陌生)

    以升序重新排列指定范围内的元素。重载版本使用自定义的比较操作。

     sort(首地址,第一个不合法地址(即末地址+1),cmp)//cmp可以缺省
     bool cmp()//可以用到结构体上
     {
            return ();
      }
    
  3. stable_sort:

    与 sort 类似,不过保留相等元素之间的顺序关系。 stable_partition:
    与 partition 类似,不过不保证保留容器中的相对顺序。 <三>删除和替换算法(15 个) copy:
    复制序列 copy_backward: 与 copy 相同,不过元素是以相反顺序被拷贝。 iter_swap:
    交换两个 ForwardIterator 的值。

<三>删除修改复制(12 个):简单操作区间元素
  1. remove:

    删除指定范围内所有等于指定元素的元素。注意,该函数不是真正删除函数。内置函数不适合使用 remove 和 remove_if 函数。

  2. remove_copy:
    将所有不匹配元素复制到一个制定容器,返回 OutputIterator 指向被拷贝的末元素的下一个位置。

  3. remove_if:

    删除指定范围内输入操作结果为 true 的所有元素。

  4. remove_copy_if:

    将所有不匹配元素拷贝到一个指定容器。

  5. replace:

    将指定范围内所有等于 vold 的元素都用 vnew 代替。

  6. replace_copy:

    与 replace 类似,不过将结果写入另一个容器。

  7. replace_if:

    将指定范围内所有操作结果为 true 的元素用新值代替。

  8. replace_copy_if:

    与 replace_if,不过将结果写入另一个容器。

  9. swap:

    交换存储在两个对象中的值。

  10. swap_range:

    将指定范围内的元素与另一个序列元素值进行交换。

  11. unique: (常用于离散化)

    清除序列中重复元素,和 remove 类似,它也不能真正删除元素。重载版本使用自定义比较操作。

  12. unique_copy: (同上)

    与 unique 类似,不过把结果输出到另一个容器。

<四>排列组合算法(2 个):提供计算给定集合按一定顺序的所有可能排列组合

以深搜的形式实现:

  1. next_permutation:

    取出当前范围内的排列,并重新排序为下一个排列。重载版本使用自定义的比较操作。

  2. prev_permutation:

    取出指定范围内的序列并将它重新排序为上一个序列。如果不存在上一个序列则返回 false。重载版本使用 自定义的比较操作。

    1
    2
    3
    4
    5
    //常以此方式使用,但时间复杂度N!这个。。。。
    do
    {
    //操作
    }while((next_permutation(首地址,第一个不合法地址)
<五>生成和异变算法(3 个)
  1. fill:
    将输入值赋给标志范围内的所有元素。
    fill(首地址,第一个不合法地址,2); //该区间内全部赋值为2
    区别于 memset,memset 是按位赋值,只能赋每位值相同值。

    1
    memset(首地址,value,(字节数)常用sizeof()获取)
  2. fill_n:

    将输入值赋给 first 到 first+n 范围内的所有元素。

    // 从开始以此赋值,3个5
    fill_n(首地址,3,5);
    
  3. transform:

    将输入的操作作用与指定范围内的每个元素,并产生一个新的序列。重载版本将操作作用在一对元素上,另外一个元素来自输入的另外一个序列。结果输出到指定容器。

    transform (原始对象首地址, 原始对象第一个不合法地址, 输出对象首地址, operate(操作函数));
    char operate(char c)//常用转化大小写,以此为例子
    {
        if (isupper(c))
        {
            return c+32;
        }
        return c;
    }
    
<六>关系算法(6 个)
  1. equal:

    如果两个序列在标志范围内元素都相等,返回 true。重载版本使用输入的操作符代替默认的等于操作符。

  2. includes:

    判断第一个指定范围内的所有元素是否都被第二个范围包含,使用底层元素的<操作符,成功返回 true。重载版本使用用户输入的函数。

  3. max:(很多人问我,这不是 cmath 吗,呃。。。。。不是)
    返回两个元素中较大一个。重载版本使用自定义比较操作。

    1
    max35)的值是5
  4. max_element:

    返回一个 ForwardIterator,指出序列中最大的元素。重载版本使用自定义比较操作。

    1
    max_element(a, a+6)  返回一个最大值位置指针
  5. min:

    返回两个元素中较小一个。重载版本使用自定义比较操作。

    1
    min35)的值是5
  6. min_element:

    返回一个 ForwardIterator,指出序列中最小的元素。重载版本使用自定义比较操作。

<七>集合算法(4 个)
  1. set_union:
    构造一个有序序列,包含两个序列中所有的不重复元素。重载版本使用自定义的比较操作。

  2. set_intersection:
    构造一个有序序列,其中元素在两个序列中都存在。重载版本使用自定义的比较操作。

  3. set_difference:
    构造一个有序序列,该序列仅保留第一个序列中存在的而第二个中不存在的元素。重载版本使用自定义的比较操作。

  4. set_symmetric_difference:

    构造一个有序序列,该序列取两个序列的对称差集(并集-交集)。

<八>堆算法(4 个)
  1. make_heap:

    把指定范围内的元素生成一个堆。重载版本使用自定义比较操作。
    
  2. pop_heap:

    并不真正把最大元素从堆中弹出,而是重新排序堆。它把 first 和 last-1 交换,然后重新生成一个堆。可使用容器的 back 来访问被”弹出”的元素或者使用 pop_back 进行真正的删除。重载版本使用自定义的比较操作。

  3. push_heap:

    假设 first 到 last-1 是一个有效堆,要被加入到堆的元素存放在位置 last-1,重新生成堆。在指向该函数前,必须先把元素插入容器后。重载版本使用指定的比较操作。

  4. sort_heap:

    对指定范围内的序列重新排序,它假设该序列是个有序堆。重载版本使用自定义比较操作。


『算法-ACM竞赛-疯子的算法总结』2 STL Ⅰ 算法 ( algorithm )
https://chiamzhang.github.io/2024/06/29/『算法-ACM竞赛-疯子的算法总结』2 STL Ⅰ 算法 ( algorithm )/
Author
Chiam
Posted on
June 29, 2024
Licensed under