CPP模版库使用

CPP模版库使用

前言

本来想写算法题一直用go,但是无奈找工作的在线OJ大部分都不支持GO吧?反正常青树就俩,C++和JAVA,第二常见的就是python和js了,其他的基本上没有了。

所以为了不让我的C++生疏,我决定写算法题的时候还是要带上C++,和GO轮流来吧,这样子两个都熟悉熟悉。而且CPP的库我都忘光怎么使用了,因此写这篇,主要是如何使用,原理可能一带而过一下吧。

两个参考链接:

https://en.cppreference.com/w/

http://www.cplusplus.com/reference/

map

参考链接:https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html

1、简介

map是STL的一个关联容器,每个key只能出现一次,第二次为value。map内部自建一颗红黑树,对数据有自动排序的功能,所以在map内部所有的数据都是有序的。

它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。对于迭代器来说,可以修改实值,而不能修改key。

自动建立Key - value的对应。key 和 value可以是任意你需要的类型。

2、功能

根据key值快速查找记录,查找的复杂度基本是Log(N),如果有1000个记录,最多查找10次,1,000,000个记录,最多查找20次。

快速插入Key -Value 记录。

快速删除记录

根据Key 修改value记录。

遍历所有记录。

3、使用

开头需要引入map

#include <map>

构造函数我们用最简单的方法

map<int, string> mapStudent;

为了使用方便,可以对模板类进行一下类型定义,

typedef map<int,CString> UDT_MAP_INT_CSTRING;

UDT_MAP_INT_CSTRING enumMap;

插入

插入一共有三种方式,有两种是insert,还有一种是根据下标。区别就是insert操作如果有key值,那么插入数据是不行的,但是用数组下标的方式,就可以插入或者更新key对应的value。

遍历

常用这种方式,或者通过迭代器

for (const auto& m : map1) {
    cout << m.first << m.second << endl;
}

查找

不建议用下标的方式直接获取,因为不存在的会返回0,不太行

建议用find

删除

移除某个map中某个条目用erase()

该成员方法的定义如下:

iterator erase(iterator it);//通过一个条目对象删除

iterator erase(iterator first,iterator last)//删除一个范围

size_type erase(const Key&key);//通过关键字删除

clear()就相当于enumMap.erase(enumMap.begin(),enumMap.end());

基本操作函数

begin() 返回指向map头部的迭代器

clear() 删除所有元素

count() 返回指定元素出现的次数

empty() 如果map为空则返回true

end() 返回指向map末尾的迭代器

equal_range() 返回特殊条目的迭代器对

erase() 删除一个元素

find() 查找一个元素

get_allocator() 返回map的配置器

insert() 插入元素

key_comp() 返回比较元素key的函数

lower_bound() 返回键值>=给定元素的第一个位置

max_size() 返回可以容纳的最大元素个数

rbegin() 返回一个指向map尾部的逆向迭代器

rend() 返回一个指向map头部的逆向迭代器

size() 返回map中元素的个数

swap() 交换两个map

upper_bound() 返回键值>给定元素的第一个位置

value_comp() 返回比较元素value的函数

示例

#include <iostream>
#include <map>
#include <string>
using namespace std;
// 遍历二,C++17 的特性
// void print_map(const map<string, int>& m) {
//     for (const auto& [key, value] : m) {
//         cout << key << " = " << value << "; " << endl;
//     }
// }

int main() {
    // 初始化1
    map<int, int> map1 = {{0, 0}, {1, 2}, {2, 3}};
    // 遍历1
    for (const auto& m : map1) {
        cout << m.first << m.second << endl;
    }
    // 插入数据1,通过下标的方式
    map1[4] = 5;

    // 插入数据2,通过insert插入pair
    map1.insert(pair<int, int>(3, 4));

    // 插入数据3,通过插入value_type
    map1.insert(map<int, int>::value_type(5, 6));

    /* 遍历2,通过迭代器
    iter = _map.begin();
    while(iter != _map.end()) {
        cout << iter->first << " : " << iter->second << endl;
        iter++;
    }
    */
    map<int, int>::iterator iter;
    for (iter = map1.begin(); iter != map1.end(); iter++) {
        cout << iter->first << ' ' << iter->second << endl;
    }

    // 存在key值的情况下,再insert就会失败,可以返回pair,第一个是迭代器,第二个为是否插入成功
    pair<map<int, int>::iterator, bool> Insert_Pair;
    Insert_Pair = map1.insert(map<int, int>::value_type(5, 6));
    cout << "Insert_Pair: " << Insert_Pair.second << endl;

    // map的大小
    int nSize = map1.size();
    cout << "size:" << nSize << endl;

    // 反向迭代器
    map<int, int>::reverse_iterator iter2;
    for (iter2 = map1.rbegin(); iter2 != map1.rend(); iter2++) {
        cout << iter2->first << "  " << iter2->second << endl;
    }

    //判断key是否存在,返回的是布尔值
    cout << "count(1)是否存在:" << map1.count(10) << endl;

    // find查找,如果有,则返回所在位置的迭代器,否则返回end位置的迭代器
    iter = map1.find(10);
    if (iter != map1.end())
        cout << "Find, the value is " << iter->second << endl;
    else
        cout << "Do not Find" << endl;
    cout << map1[100] << endl;

    // 删除元素
    iter = map1.find(1);
    map1.erase(iter);

    //如果要删除1,用关键字删除
    int n = map1.erase(1);  //如果删除了会返回1,否则返回0

    //用迭代器,成片的删除,一下代码把整个map清空
    map1.erase(map1.begin(), map1.end());

    //
    cout <<"map是否为空;"<< map1.empty() << endl;
   
    return 0;
}

unordered_map

参考链接:https://blog.csdn.net/qq_21997625/article/details/84672775

与map用起来扎不多吧,但是有些不同

1、引入的头文件不同

#include < unordered_map >

2、内部实现机理不同

unordered_map是通过哈希表(散列表),查找的时间复杂度可达O(1),元素的排序是无序的。

3、优缺点对比

map:

优点:有序性,因此效率非常的高

缺点: 空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间

适用处:对于那些有顺序要求的问题,用map会更高效一些

unordered_map:

优点: 因为内部实现了哈希表,因此其查找速度非常的快

缺点: 哈希表的建立比较耗费时间

适用处:对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map

vector

参考链接:https://www.runoob.com/w3cnote/cpp-vector-container-analysis.html

1、简介

向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。

2、特性

初始化默认内存容量是0,有第一个元素时开辟一个元素大小,接下来的扩容以2倍的大小自动增长。(VS下是1.5倍)

1.顺序序列

顺序容器中的元素按照严格的线性顺序排序。可以通过元素在序列中的位置访问对应的元素。

2.动态数组

支持对序列中的任意元素进行快速直接访问,甚至可以通过指针算述进行该操作。提供了在序列末尾相对快速地添加/删除元素的操作。

3.能够感知内存分配器的(Allocator-aware)

容器使用一个内存分配器对象来动态地处理它的存储需求。

3、功能

1.构造函数

  • vector():创建一个空vector
  • vector(int nSize):创建一个vector,元素个数为nSize
  • vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t
  • vector(const vector&):复制构造函数
  • vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中

2.增加函数

  • void push_back(const T& x):向量尾部增加一个元素X
  • iterator insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素x
  • iterator insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素x
  • iterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据

3.删除函数

  • iterator erase(iterator it):删除向量中迭代器指向元素
  • iterator erase(iterator first,iterator last):删除向量中[first,last)中元素
  • void pop_back():删除向量中最后一个元素
  • void clear():清空向量中所有元素

4.遍历函数

  • reference at(int pos):返回pos位置元素的引用
  • reference front():返回首元素的引用
  • reference back():返回尾元素的引用
  • iterator begin():返回向量头指针,指向第一个元素
  • iterator end():返回向量尾指针,指向向量最后一个元素的下一个位置
  • reverse_iterator rbegin():反向迭代器,指向最后一个元素
  • reverse_iterator rend():反向迭代器,指向第一个元素之前的位置

5.判断函数

  • bool empty() const:判断向量是否为空,若为空,则向量中无元素

6.大小函数

  • int size() const:返回向量中元素的个数
  • int capacity() const:返回当前向量所能容纳的最大元素值
  • int max_size() const:返回最大可允许的vector元素数量值

7.其他函数

  • void swap(vector&):交换两个同类型向量的数据
  • void assign(int n,const T& x):设置向量中前n个元素的值为x,(会重新分配地址,草了)
  • void assign(const_iterator first,const_iterator last):向量中[first,last)中元素设置成当前向量元素

8.看着清楚

1.push_back 在数组的最后添加一个数据

2.pop_back 去掉数组的最后一个数据

3.at 得到编号位置的数据

4.begin 得到数组头的指针

5.end 得到数组的最后一个单元+1的指针

6.front 得到数组头的引用

7.back 得到数组的最后一个单元的引用

8.max_size 得到vector最大可以是多大

9.capacity 当前vector分配的大小

10.size 当前使用数据的大小

11.resize 改变当前使用数据的大小,如果它比当前使用的大,者填充默认值

12.reserve 改变当前vecotr所分配空间的大小

13.erase 删除指针指向的数据项

14.clear 清空当前的vector

15.rbegin 将vector反转后的开始指针返回(其实就是原来的end-1)

16.rend 将vector反转构的结束指针返回(其实就是原来的begin-1)

17.empty 判断vector是否为空

18.swap 与另一个vector交换数据

4、使用

头文件导入#include <vector>

初始化

#include <iostream>
#include <vector>
using namespace std;

int main() {
    // 1、默认初始化,为空,size为0,capacity也为0
    vector<int> v1;

    // 2、拷贝初始化,具有相同的容量和元素
    vector<int> v2 = v1;

    // 2.1、复制构造函数拷贝
    vector<int> v2_1(v1);

    // 3、列表中元素的拷贝
    vector<int> v3 = {1, 2, 3, 4, 5, 6, 7};

    // 4、指定元素中的拷贝
    vector<int> v4(v3.begin() + 2, v3.end() - 1);

    // 4.1 拷贝数组的元素
    int a[4] = {1, 2, 3, 4};
    vector<int> v4_1(a + 1, a + 3);

    // 5、指定vector的长度,元素进行缺省初始化
    vector<int> v5(7);

    // 6、指定长度和元素的初始化值
    vector<int> v6(7, 3);
    return 0;
}

二维数组初始化,和上面的类似

int N = 5, M = 6;
vector<vector<int> > obj(N, vector<int>(M, 0));  //定义二维动态数组5行6列

遍历

一维数组

// 1、下标遍历
vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int i = 0; i < vec.size(); i++) {
    cout << vec[i] << " ";
}
cout << endl;

// 2、C++11的遍历方式
for (int &v : vec) {
    cout << v << " ";
}
cout << endl;

// 3、迭代器遍历
vector<int>::iterator it;
for (it = vec.begin(); it != vec.end(); it++) {
    cout << *it << " ";
}
cout << endl;

二维数组遍历

    // 1、二维数组通过下标遍历
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            cout << obj[i][j] << " ";
        }
        cout << endl;
    }

    // 2、通过C++11的特性遍历
    for (auto &i : obj) {
        for (int &j : i) {
            cout << j << " ";
        }
        cout << endl;
    }

    // 3、通过迭代器遍历
    vector<vector<int>>::iterator it1;
    vector<int>::iterator it2;
    for (it1 = obj.begin(); it1 != obj.end(); it1++) {
        for (it2 = (*it1).begin(); it2 != (*it1).end(); it2++) {
            cout << (*it2) << " ";
        }
        cout << endl;
    }
    cout << endl;

5、问题

自己想到一个问题,vector都有push_back()和pop_back()两个操作,为什么不能当做栈来使用,果然知乎大神已经答过了,https://www.zhihu.com/question/378846608/answer/1074026357

std::vector 是容器,而 std::stack容器适配器

std::stack只提供和堆栈相关的接口,其中主要是push()emplace()pop()top()empty()。使用 std::stack时只需关心这些接口,而非内在用了哪个具体容器。改变容器类型也不需要修改调用方的代码。这个设计应该可说是乎合 SOLID 中的 I ──接口隔离原则(interface segregation principle)。

std::stack 可适配的标准容器有std::vectorstd::liststd::deque,而 std::deque 是缺省的,因为它提供O(1)的push_back(),而 std::vector::push_back()均摊(amortized) O(1) 。

这里有两个概念:容器和容器适配器。

STL的顺序容器,分别是

  1. vector 向量容器,底层是由数组实现的。
  2. list 双向链表容器,底层由双向链表实现(含尾节点(为了保证迭代器的通用性))
  3. deque 双端队列容器,底层用双端队列实现 【可以下标操作】

STL的关联容器分别是

  1. set单重集合
  2. msp 映射表
  3. 散列表 hashtable (unordered_map?)

容器适配器

  1. 栈stack,底层使用deque,将一端封闭,实现后进先出的行为。
  2. 队列queue,底层默认使用deque的数据结构,封闭低端的出口和顶端的入口,实现先进先出
  3. 带权的队列qriority_queue,默认情况下qriority_queue底层使用max_heap(以vector)实现,可以满足以“权值高低自动排序”的特性。

queue

queue是容器适配器,和容器用起来有点不一样,不能直接初始化。

只能访问 queue<T> 容器适配器的第一个和最后一个元素。只能在容器的末尾添加新元素,只能从头部移除元素。

  • front():返回 queue 中第一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
  • back():返回 queue 中最后一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
  • push(const T& obj):在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
  • push(T&& obj):以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。
  • pop():删除 queue 中的第一个元素。
  • size():返回 queue 中元素的个数。
  • empty():如果 queue 中没有元素的话,返回 true。
  • emplace():用传给 emplace() 的参数调用 T 的构造函数,在 queue 的尾部生成对象。
  • swap(queue &other_q):将当前 queue 中的元素和参数 queue 中的元素交换。它们需要包含相同类型的元素。也可以调用全局函数模板 swap() 来完成同样的操作。

queue 也没有迭代器。访问元素的唯一方式是遍历容器内容,并移除访问过的每一个元素。

#include <iostream>
#include <queue>
using namespace std;
int main() {
    deque<double> values{1.5, 2.5, 3.5, 4.5};
    // 复制其底层deque的值
    queue<double> numbers(values);

    queue<pair<int, int>> que;
    // push添加操作,拷贝构造好的
    que.push(make_pair(1, 2));
    // que.push(pair<int, int>(1, 2));
    // 直接调用构造函数,直接构造一个,节省内存空间
    que.emplace(3, 4);

    // 大小
    cout << "size: " << que.size() << endl;

    // 取出最后一个值
    cout << "新添加的值:" << que.back().first << " " << que.back().second
         << endl;

    // 访问元素只能遍历一遍
    while (!que.empty()) {
        cout << que.front().first << " " << que.front().second << endl;
        // 弹出第一个
        que.pop();
    }

    return 0;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇