C++STL标准库学习笔记(十四)算法(下)

这篇博客详细介绍了STL中的几种算法,包括删除算法(remove、remove_if等)、变序算法(reverse、rotate等)、排序算法(sort、stable_sort等)以及有序区间算法(binary_search、lower_bound等)。文中通过实例代码解释了每个算法的工作原理和使用方法,强调了算法的时间复杂度和适用范围。

目录

前言:

正文:

2.3. 删除算法

2.4. 变序算法

2.5. 排序算法

2.6. 有序区间算法

后记:


前言:

        在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标记了出来。

        在这一篇文章中,我们主要对STL中的算法进行简单的介绍,这篇文章接着上一篇文章(上一篇文章在这里)。

正文:

2.3. 删除算法

        删除容器中的某些元素

        删除--不会使容器中的元素减少

                将所有应该被删除的元素看作空位置

                用留下的元素从后往前移,依次去填空位置

                元素前移后,它原来的位置也就算是空位置

                也应由后面的留下的元素来填上

                最后,没有被填上的空位置,维持其原来的值不变

        删除算法不应作用于关联容器

        remove:删除区间中等于某个值的元素

        remove_if:删除区间中满足某种条件的元素

        remove_copy:拷贝区间到另一个区间,等于某个值的元素不拷贝

        remove_copy_if:拷贝区间到另一个区间,符合某种条件的元素不拷贝

        unique:删除区间中连续相等的元素,只留下一个(可自定义比较器),unique返回最后一个元素后面的迭代器,用它减begin就可以知道长度

        unique_copy:拷贝区间到另一个区间,连续相等的元素,只拷贝第一个到目标区间()

算法复杂度都是O(n)

unique

        template<class Fwdit>

        Fwdit unique(Fwdit first, Fwdit last);

                用==判断是否相等

        templast<class Fwdit, class Pred>

        Fwdit unique(Fwdit first, Fwdit last, Pred pr);

                 用pr(x,y)为true说明x和y相等

                对[first, last)这个序列中连续相等的元素,只留下第一个

                返回值是迭代器,指向元素删除后的区间的最后一个元素的后面

        样例:(remove的)

#include<iostream>
#include<vector>
#include<iterator>
#include<algorithm>
using namespace std;
int main(int argc, char const *argv[])
{
    int a[5] = {1,2,3,2,5};
    int b[6] = {1,2,3,2,5,6};
    ostream_iterator<int> oit(cout,",");
    int* p = remove(a,a+5,2);
    cout<<"1)";
    copy(a,a+5,oit);
    cout<<endl;//输出:1)1,3,5,2,5,//这里就能看出前面说的删除移位了
    cout<<"2)"<<p-a<<endl;//输出:2)3
    vector<int> v(b,b+6);
    remove(v.begin(),v.end(),2);
    cout<<"3)";
    copy(v.begin(),v.end(),oit);
    cout<<endl;//输出:3)1,3,5,6,5,6,
    cout<<"4)";
    cout<<v.size()<<endl;//输出:4)6
    return 0;
}

        样例中删除了a中的两个“2”,结果导致3前移了一位,而5补上了3原来的位置,而另一次删除的时候,vector的大小是没动的。

2.4. 变序算法

        变序算法改变容器中的元素的顺序

        但是不改变元素的值

        变序算法不适用于关联容器

        算法复杂度都是O(n)的

        reverse:颠倒区间的前后次序

        reverse_copy:把一个区间颠倒后的结果拷贝到另一个区间,源区间不变

        rotate:将区间进行循环后移

        rotate_copy:将区间以首尾相接的形式进行旋转后的结果拷贝到另一个区间,源区间不变。

        next_permutation:将区间改为下一个排列(可自定义比较器)(如123,132,213之类,这是排列,而它们之间有大小关系,这就是上一个(前面小)下一个(前面大)了)

        prev_permutation:将区间改为上一个排列(可自定义比较器)

        random_shuffle:随机打乱区间内元素的顺序

        partition:把区间内满足某个条件的元素移到前面,不满足该条件的移到后面

stable_patition

        把区间内满足某个条件的元素移到前面

        不满足该条件的移到后面

        而对这两部分元素,分别保持它们原来的先后次序不变

random_shuffle

        template<class Ranit>

        void random_shuffle(Ranit first, Ranit last);

        随机打乱[first, last)中的元素,适用于能随机访问的容器

reverse

        template<class Bidlt>

        void reverse(Bidlt first, Bidlt last);

        颠倒[first,last)顺序

next_permutation

        template<class init>

        bool next_permutaion(init first, init last);

        求下一个排列

        样例:(next_permutation)

#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
int main(int argc, char const *argv[])
{
    string str = "231";
    char szStr[] = "324";
    while (next_permutation(str.begin(),str.end()))
    {
        cout<<str<<endl;
    }
    cout<<"****"<<endl;
    while (next_permutation(szStr,szStr+3))
    {
        cout<<szStr<<endl;
    }
    sort(str.begin(),str.end());
    cout<<"****"<<endl;
    while (next_permutation(str.begin(),str.end()))
    {
        cout<<str<<endl;
    }
    
    return 0;
}
/*
输出;
312
321
****
342
423
432
****
132
213
231
312
321
*/

        样例2:(也是这个函数的)

#include<iostream>
#include<algorithm>
#include<string>
#include<list>
#include<iterator>
using namespace std;
int main(int argc, char const *argv[])
{
    int a[] = {8,7,10};
    list<int> ls(a,a+3);
    while (next_permutation(ls.begin(),ls.end()))
    {
        list<int>::iterator i;
        for ( i = ls.begin(); i != ls.end(); i++)
        {
            cout<<*i<<" ";
        }
        cout<<endl;
    }
    
    return 0;
}

2.5. 排序算法

        比前面的变序算法复杂度更高,一般是O(nlog(n))

        排序算法需要随机访问迭代器的支持

        不适用于关联容器和list

        sort:将区间从小到大排序(可自定义比较器)

        stable_sort:将区间从小到大排序,并保持相等元素之间的相对次序(可自定义比较器)

        partial_sort:对区间部分排序,直到最小的n个元素就位(可自定义比较器)

        partial_sort_copy:将区间前n个元素的排序结果拷贝到别处,源区间不变(可自定义比较器)

        nth_element:对区间部分排序,使得第n小的元素(n从0开始算)就位,而且比它小的都在它前面,比它大的都在它后面(可自定义比较器)

        make_heap:使区间成为一个“堆”(可自定义比较器)

        push_heap:将元素加入一个“堆”区间(可自定义比较器)

        pop_heap:从“堆”区间删除堆顶元素(可自定义比较器)

        sort_heap:将一个“堆”区间进行排序,排序结束后,该区间就是普通的有序区间,不再是“堆”了(可自定义比较器)

sort快速排序

        template<class Ranlt>

        void sort(Ranlt first, Ranlt last)

                按升序排序

                判断x是否应比y靠前,就看x<y是否为true

        template<class Ranlt, class Pred>

        void sort(Ranlt first, Ranlt last, Pred pr);

                按升序排序

                判断x是否应比y靠前,就看pr(x,y)是否为true

        (这些前面的笔记也是有讲过用法的)

        样例:

#include<iostream>
#include<algorithm>
using namespace std;
class Myless
{
public:
    bool operator()(int n1,int n2)
    {
        return(n1 % 10)<(n2 % 10);
    }
};

int main(int argc, char const *argv[])
{
    int a[] = {14,2,9,111,78};
    sort(a,a+5,Myless());
    int i;
    for ( i = 0; i < 5; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;//输出:111 2 14 78 9 
    sort(a,a+5,greater<int>());
    for ( i = 0; i < 5; i++)
    {
        cout<<a[i]<<" ";
    }
    //输出:111 78 14 9 2
    return 0;
}

        sort实际上是快速排序,时间复杂度O(n*log(n))

                平均性能最优

                但是最坏的情况下,性能可能非常差

        如果要保证“最坏情况下”的性能,那么可以使用

        stable_sort

        stable_sort实际上是归并排序,特点是能保持相等元素之间的先后次序

        在有足够存储空间的情况下,复杂度为n*log(n),否则复杂度为n*log(n)*log(n)

        stable_sort用法和sort相同

        排序算法要求随机存取迭代器的支持,所以list不能使用排序算法,要使用list::sort

2.6. 有序区间算法

        要求所操作的区间是已经从小到大排好序的

        要求随机访问迭代器的支持

        有序区间算法不能用于关联容器和list

        binary_search:判断区间中是否包含某个元素

        includes:判断是否一个区间中的每个元素,都在另一个区间中

        lower_bound:查找最后一个不小于某值的元素的位置

        upper_bound:查找第一个大于某值的元素的位置

        equal_range:同时获取lower_bound和upper_bound

        merge:合并两个有序区间到第三个区间

        (这些东西在我的之前的笔记中有提到过,红色字体的部分都是Olog(n)的,include要看区间大小,mergeO(n))

        set_union:将两个有序区间的并拷贝到第三个区间

        set_intersection:将两个有序区间的交拷贝到第三个区间

        set_difference:将两个有序区间的差拷贝到第三个区间

        set_symmetric_difference:将两个有序区间的对称差拷贝到第三个区间

        inplace_merge:将两个连续的有序区间原地合并为一个有序区间(就合并嘛)

binary_search

        折半查找

        要求容器已经有序且支持随机访问迭代器,返回是否找到

        template<class Fwdlt, class T>

        bool binary_search(Fwdlt first, Fwdlt last, const T& val);

        上面这个版本,比较两个元素x,y大小时,看x<y

        template<class Fwdlt, class T, class Pred>

        bool binary_search(Fwdlt first, Fwdlt last, const T& val, Pred pr);

        上面这个版本,比较两个元素x,y大小时,若pr(x,y)为true,则认为x小于y

lower_bound

        template<class Fwdlt, class T>

        Fwdlt lower_bound(Fwdlt first, Fwdlt last, const T& val);

        要求[first,last)是有序的

        查找[first,last) 中的,最大的位置Fwdlt,使得[first,Fwdlt)中所有的元素都比val小

equal_range

        template<class Fwdlt, class T>

        pair<Fwdlt, Fwdlt> equal_range(Fwdlt first, Fwdlt last, const T& val);

        要求[first,last)是有序的,

        返回值是一个pair,假设为p,则:

        [first,p.first)中的元素都比val小

        [p.second,last)中的所有元素都比val大

        p.first就是lower_bound的结果

        p.last就是upper_bound的结果

        (照我看就只有最后两句有用哈哈哈,毕竟最后两句浅显易懂)

merge

        template<class lnlt1, class lnlt2, class Outlt>

        Outlt merge(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2,Outlt x);

        用<作比较器

        template<class lnlt1, class lnlt2, class Outlt, class Pred>

        Outlt merge(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x, Pred pr);

        用pr作比较器

        把[first1,last1),[first2,last2)两个升序序列合并,形成第三个升序序列,第三个升序序列以x开头

includes

        template<class lnlt1, class lnlt2>

        bool includes(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2);

        template<class lnlt1, class lnlt2, class Pred>

        bool includes(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Pred pr);

        判断[first2,last2)中的每个元素,是否都在[first1, last1)中

        第一个用<作比较器

        第二个用pr作比较器,pr(x,y)==true说明x,y相等

set_difference

        template<class lnlt1, class lnlt2, class Outlt>

        Outlt set_difference(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x);

        template<class lnlt1, class lnlt2, class Outlt, class Pred>

        Outlt set_difference(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x, Pred pr);

        (这些模板敲得挺累,其实就是为了说明它们应该传入什么参数)

        求出[first1,last1)中,不在[first2,last2)中的元素,放到从x开始的地方

        如果[first1,last1)里有多个相等元素不在[first2,last2)中,则这多个元素也都会被放入x代表的目标区间里

set_intersection

        template<class lnlt1, class lnlt2, class Outlt>

        Outlt set_intersection(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x);

        template<class lnlt1, class lnlt2, class Outlt, class Pred>

        Outlt set_intersection(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x, Pred pr);

        求出[first1,last1),[first2,last2)中共有的元素,放到从x开始的地方

        如果某个元素e在[first1,last1)里出现n1次,在[first2,last2)中出现n2次,则该元素在目标区间里出现min(n1,n2)次

set_symmetric_difference

        template<class lnlt1, class lnlt2, class Outlt>

        Outlt set_symmetric_difference(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x);

        template<class lnlt1, class lnlt2, class Outlt, class Pred>

        Outlt set_symmetric_difference(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x, Pred pr);

        把两个区间相互不在另一区间里的元素放入x开始的地方

set_union

        template<class lnlt1, class lnlt2, class Outlt>

        Outlt set_union(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2,Outlt x);

        用<比较大小

        template<class lnlt1, class lnlt2, class Outlt, class Pred>

        Outlt set_union(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x, Pred pr);

        用pr比较大小

        求两个区间的并,放到x开始的位置

        如果某个元素e在[first1,last1)里出现n1次,在[first2,last2)中出现n2次,则该元素在目标区间里出现max(n1,n2)次

无法分类的东东:

bitset

        template<size_t N>

        class bitset

        {

        .......

        }

        实际使用的时候,N是一个常型整数

        如:

        bit<40>bst;

        bst是一个由40位组成的对象

        用bitset的函数可以方便地访问任何一位

        bitset的成员函数:

        bitset<N>&operator&=(const bitset<N>& rhs);

        bitset<N>&operator|=(const bitset<N>& rhs);

        bitset<N>&operator^=(const bitset<N>& rhs);

        bitset<N>&operator<<=(size_t num);

        bitset<N>&operator>>=(size_t num);

        (位运算)

        bitset<N>&set();//全部设成1

        bitset<N>&set(size_t pos, bool val = true);//设置某位

        bitset<N>&reset();//全部设成0

        bitset<N>&set(size_t pos);//设置某位为0

        bitset<N>&flip();//全部翻转

        bitset<N>&flip(size_t pos);//翻转某位

        reference operator[](size_t pos);//返回对某位的引用

        bool operator[](size_t pos)const;//判断某位是否为1

        reference at(size_t pos);

        bool at(size_t pos)const;

        unsigned long to_ulong()const;//转换成整数

        string to_string()const;//转换成字符串

        size_t count()const;//计算1的个数

        size_t size()const;

        bool operator==(const bitset<N>& rhs)const;

        bool operator!=(const bitset<N>& rhs)const;(判断两个是否不等)

        bool test(size_t pos)const;//测试某位是否为1

        bool any()const;//是否有某位为1

        bool none()const;//是否全部为0

        bitset<N> operator<<(size_t pos)const;

        bitset<N> operator>>(size_t pos)const;

        bitset<N> operator~()const;

        static const size_t bitset_size = N;

        注意:第0位在最右边

后记:

        long long说的就是这两集,实在太长了啊啊啊啊,不过也好,整理出来了完整的笔记,最近过年摸鱼也没少摸,从老家回来后就要回归高效率的生活了,希望新的一年里能做到最好吧。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值