c++学习笔记(九、deque、stack、queue、list、set/multiset)

今天就学这5种容器:deque、stack、queue、list、set/multiset,学了这5种之后也差不多,明天就能学完了。但是今天这一篇,只要内容还是看了传智播客c++的视频,写的笔记,有些截图都是从视频截取的,如果想去认真学习容器的,可以看看这个一个视频,个人感觉还不错。

9.1 deque

9.1.1 deque介绍

deque 也是顺序容器的一种,同时也是一个可变长数组。要使用 deque,需要包含头文件 deque。所有适用于 vector 的操作都适用于 deque。

deque 和 vector 有很多类似的地方。在 deque 中,随机存取任何元素都能在常数时间内完成(但慢于vector)。它相比于 vector 的优点是,vector 在头部删除或添加元素的速度很慢,在尾部添加元素的性能较好,而 deque 在头尾增删元素都具有较好的性能(大多数情况下都能在常数时间内完成)。

deque是双口容器,可以在头部插入或删除,也可以在尾部插入或删除。deque没有容量的概念,因为它是动态的以分段的连续空间组合而成,随时可以增加一段新的空间并链接起来。

在这里插入图片描述
上面这幅图,画的就是deque的操作,记图比记文字快。

在这里插入图片描述
这个图是deque内存管理的,deque是自己管理内存的申请和释放,不需要像vector那样要我们手动释放,deque内存管理是这样,它自己有一个中继器,通俗来说,就是一个指针数组,里面存储着一系列的地址,这里面的地址,就是指向一块内存区域,这一块内存区域才是存储数据用的,如果一条用完了,那就再申请一条,然后把这个内存地址保存到中继器中,由中继器统一管理。

特性总结:

  • 双端插入和删除元素效率较高
  • 指定位置插入也会导致数据元素移动,降低效率
  • 可随机存储,效率高
9.1.2 deque构造函数

在这里插入图片描述
构造方法和vertor差不多,不写了,下面的函数如果跟vertor差不多就不写例子了。

9.1.3 deque赋值操作

在这里插入图片描述
这个swap交换,只是一个简单的交换,不需要跟vertor一样,需要减少空间。

9.1.4 deque大小操作

在这里插入图片描述
这个就没有提供容量的接口,还有申请内存的大小,因为是deque自己管理了。

9.1.5 deque双端插入和删除

在这里插入图片描述
vertor容器只能单端插入,不同于我们deque容器,是两端都可以插入和删除。

9.1.6 deque数据存取

在这里插入图片描述
这个就跟vertor容器一模一样了。

9.1.7 deque插入操作

插入操作,不能截图,不过跟vertor是差不多的
insert方法

9.1.8 deque删除操作

在这里插入图片描述
deque容器就讲到这里,不过传智播客还讲了一个很好的例子,我这里偷懒一下了,以后有空再回来写写。

9.2 stack

9.2.1 stack介绍

stack是一种先进后出的数据结构,它只有一个出口,就是我们平时说的栈,只能在栈顶添加元素,移除元素,获取顶端元素,只有栈顶元素才能被使用,所以说栈不具有遍历行为,没有迭代器。
在这里插入图片描述
这个就是栈的数据结构图。

特性总结:
栈不能遍历,不支持随机访问,只能通过top从栈顶获取或删除元素。

9.2.2 stack构造函数

在这里插入图片描述

9.2.3 stack赋值操作

在这里插入图片描述

9.2.4 stack数据存取操作

在这里插入图片描述

9.2.5 stack大小操作

在这里插入图片描述

栈容器的操作比较简单,就只有入栈出栈操作,但是栈的用处还是有的,这里就不举例了,有空可以去刷leetcode了。

9.3 queue

9.3.1 queue介绍

队列跟栈一样比较简单,没太多好讲的,队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。

队列是一种先进先出(First in First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。在这里插入图片描述

9.3.2 queue构造函数

在这里插入图片描述

9.3.3 queue存取、插入和删除操作

在这里插入图片描述
跟栈的区别就是队列可以两端操作,栈只有一端。

9.3.4 queue赋值操作

在这里插入图片描述
赋值语句都是重载=操作符。

9.3.5 queue大小操作

在这里插入图片描述
队列也比较简单,可以去leetcode刷刷题,用c在leetcode刷题太累了,啥都要自己写,现在可以转移到c++刷题了。

9.4 list

9.4.1 list介绍

ist 是顺序容器的一种。list 是一个双向链表。使用 list 需要包含头文件 list。双向链表的每个元素中都有一个指针指向后一个元素,也有一个指针指向前一个元素,如图所示。
在这里插入图片描述
链表的内存是不连续的,需要一个结点的时候,才申请一个结点的内存,所以链表需要一个额外的空间保存,前一个的指针还有后一个的指针。

9.4.2 list构造函数

在这里插入图片描述

9.4.3 list数据元素插入和删除操作

在这里插入图片描述
这里新加了一个remove办法,remove就是删除容器中所有与elem匹配的元素。
list容器不支持随机访问,只能往后++,所以插入的时候,不能直接用迭代器+2,只能++到指定位置。
写个例子:

#include <list>
void show(list<int>& v) {
    for(list<int>::iterator it = v.begin(); it != v.end(); it++) {
            cout << *it << " ";
    }
    cout << endl;
}

int main(int argc, char **argv)
{
    list<int> list;
    list.push_back(10);
    list.push_back(20);
    list.push_back(30);
    list.push_back(40);
    list.push_back(50);

    show(list);
    
    list.insert(list.begin(), 1);

    show(list);

    //list.insert(list.begin() + 1, 2);  //失败。不能随机访问
    list.insert(list.begin()++, 2);    //只支持++操作

    show(list);

    return 0;
}
9.4.4 list大小操作

在这里插入图片描述
链表也没有容量的概念。

9.4.5 list赋值操作

在这里插入图片描述

9.4.6 list数据存取

在这里插入图片描述
这个是双向链表,支持获取头尾元素。

9.4.7 list反转排列排序

在这里插入图片描述
排序有点重要,例子:

bool compare1(int v1, int v2) {
return v1 > v2;    			//回调函数处理  从大到小
}

int main(int argc, char **argv)
{
    list<int> list;
    list.push_back(10);
    list.push_back(20);
    list.push_back(30);
    list.push_back(40);
    list.push_back(50);

    show(list);
    
    list.insert(list.begin(), 1);

    show(list);

    //list.insert(list.begin() + 1, 2);  //失败。不能随机访问
    list.insert(list.begin()++, 2);    //只支持++操作

    show(list);

    list.sort();    //排序,默认从小到大

    show(list);

    //可以修改成从大到小,需要传入回调函数
    list.sort(compare1);    

    show(list);

    return 0;
}

算法中也有一个sort方法,那个sort方法是支持随机访问的,也就是算法内部会根据是随机访问的,优化一下排序算法,但是list不支持随机访问,所以list自己提供了一个成员方法。

9.5 set/multiset

9.5.1 set/multiset介绍

set 是关联容器的一种,是排序好的集合(元素已经进行了排序)。set 和 multiset 类似,它和 multiset 的差别在于 set 中不能有重复的元素。multiset 的成员函数 set 中也都有。

set容器其实就是一颗搜索二叉树,我也不明白为什么叫set,而不是tree,有关树的概念可以看我另一篇博客https://blog.csdn.net/C1033177205/article/details/103414388,比较详细介绍了搜索二叉树。

set是以红黑树为底层的一种机制,(要想了解红黑树可以看了另一篇博客https://blog.csdn.net/C1033177205/article/details/103753436,这里详细讲述了红黑树),红黑树查找效率比较高。

我们不能直接修改 set 容器中元素的值。因为元素被修改后,容器并不会自动重新调整顺序,于是容器的有序性就会被破坏,再在其上进行查找等操作就会得到错误的结果。因此,如果要修改 set 容器中某个元素的值,正确的做法是先删除该元素,再插入新元素。

9.5.2 set/multiset构造函数

在这里插入图片描述

9.5.3 set/multiset赋值操作

在这里插入图片描述

9.5.4 set/multiset大小操作

在这里插入图片描述

9.5.5 set/multiset插入删除操作

在这里插入图片描述

9.5.6 set/multiset查找操作

在这里插入图片描述
二叉搜索树就是查找比较重要,练习练习:

int main(int argc, char **argv)
{
     set<int> s1;
     s1.insert(10);
     s1.insert(4);
     s1.insert(2);
     s1.insert(90);
     s1.insert(33);

     show(s1);

     set<int>::iterator it1 = s1.find(4);
     if(it1 == s1.end()) {    //返回的迭代器要跟end()判断
         //如果等于,就相当于没找到
         cout << "没找到" << endl;
     } else {
         cout << "it1 " << *it1 << endl;
     }

     set<int>::iterator it2 = s1.find(12);
     if(it2 == s1.end()) {    //返回的迭代器要跟end()判断
         //如果等于,就相当于没找到
         cout << "没找到" << endl;
     } else {
         cout << "it2 " << *it2 << endl;
     }

     //返回第一个key>=keyElem元素的迭代器
     set<int>::iterator it3 = s1.lower_bound(10);
     if(it3 == s1.end()) {    //返回的迭代器要跟end()判断
         //如果等于,就相当于没找到
         cout << "没找到" << endl;
     } else {
         cout << "it3 " << *it3 << endl;
     }
     
     //返回第一个key>keyElem元素的迭代器
     set<int>::iterator it4 = s1.upper_bound(10);
     if(it4 == s1.end()) {    //返回的迭代器要跟end()判断
         //如果等于,就相当于没找到
         cout << "没找到" << endl;
     } else {
         cout << "it4 " << *it4 << endl;
     }

     //equal_range 返回的是 lower_bound 和 upper_bound 的值 
     pair<set<int>::iterator, set<int>::iterator> it5 = s1.equal_range(10);
     if(it5.first == s1.end()) {    //返回的迭代器要跟end()判断
         //如果等于,就相当于没找到
         cout << "没找到" << endl;
     } else {
         cout << "it5.first " << *(it5.first) << endl;
     }

     if(it5.second == s1.end()) {    //返回的迭代器要跟end()判断
         //如果等于,就相当于没找到
         cout << "没找到" << endl;
     } else {
         cout << "it5.second " << *(it5.second) << endl;
     }

     return 0;
 }

运行的结果:
在这里插入图片描述

9.5.7 pair对组

对组(pair)将一对值组合成一个值,这一对值可以具有不同的数据类型,两个值可以分别用pair的两个公有函数first和second访问。

类模板:template<class T1, class T2> strcut pair;

在这里插入图片描述
创建对组的方法也比较简单,这里就不举例了。

9.5.8 改变默认排序

multiset 类模板的定义如下:

template <class Key, class Pred = less<Key>, class B = allocator<Key> > class multiset {
    ...
};

该模板有三个类型参数:Key、Pred 和 B。类型参数可以有默认值,默认值就是某种类型。例如,Pred 类型参数的默认值就是 less 类型,B 的默认值就是 allocator 类型。第三个类型参数极少用到,一般都用默认值,因此这里不做介绍。

第一个类型参数说明 multiset 容器中的每个元素都是 Key 类型的。第二个类型参数 Pred 用于指明容器中元素的排序规则,在被实例化后,Pred 可以是函数对象类,也可以是函数指针类型。

multiset 内部在排序时定义了一个变量Pred op,根据表达式op(x, y)来比较两个元素 x、y 的大小。该表达式的值为 true,则说明 x 比 y 小。Pred 的默认值是 less,less 是 STL 中的函数对象类模板,其定义如下:

template <class_Tp>
struct less
{
    bool operator() (const _Tp &__x, const _Tp &__y) const
    { return __x < __y; }
};

参考文章:http://c.biancheng.net/view/386.html

所以如果我们要修改默认参数的话,就要传入这个参数,例:

#include <set>
class compare1{
public:
    bool operator()(int v1, int v2) {
        return v1 > v2;    //这里就是回调这个函数,进行排序
    }
};

void show(set<int, compare1>& v) {
    for(set<int, compare1>::iterator it = v.begin(); it != v.end(); it++) {
            cout << *it << " ";
    }
    cout << endl;
}

int main(int argc, char **argv)
 {
     set<int, compare1> s1;
     s1.insert(10);
     s1.insert(4);
     s1.insert(2);
     s1.insert(90);
     s1.insert(33);

     show(s1);

     return 0;
 }

本来这里就要完成了,我再写一个用set容器存储类:

#include <set>
class Person
{
public:
    int age;
    int id;

    Person(int age, int id): age(age), id(id){}

    //set容器排序,需要用到<号,也可以像上面那个用仿函数
    bool operator<(const Person& anthor) const {
        return this->age > anthor.age;
    }
};

//用这种也可以
class compare1{
public:
    bool operator()(const Person& p1, const Person& p2) {
        return p1.age < p2.age;
    }
};

void show(set<int, compare1>& v) {
    for(set<int, compare1>::iterator it = v.begin(); it != v.end(); it++) {
            cout << *it << " ";
    }
    cout << endl;
}


int main(int argc, char **argv)
{
    set<Person, compare1> s1;
    Person p1(10, 1), p2(3, 2),p3(46, 3),p4(5, 4);
    s1.insert(p1);
    s1.insert(p2);
    s1.insert(p3);
    s1.insert(p4);

    for(set<Person, compare1>::iterator it = s1.begin(); it != s1.end(); it++) {
            cout << " 年龄 " << (*it).age << " id " << (*it).id << endl;
    }

    //find方法,因为这是一个类对象,find查找是谁呢?
    //其实find查找的是,你用了那个变量排序,就用那个变量匹配,不会管其他变量的。
    Person p5(10, 44);
    set<Person, compare1>::iterator it1 = s1.find(p5);
    if(it1 == s1.end()) {
        cout << "没有找到" << endl;
    } else {
        cout << "找到 年龄 " << (*it1).age << " id " << (*it1).id << endl;
    }
    
    return 0;
}

执行的结果:
在这里插入图片描述
从结果看仿函数其到作用了,还有找到的也是只是查找你排序时候的变量,其他的变量就不理会了。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值