[算法上] 5. STL“容器“的使用与练习

STL 六大组件

  1. 容器
  2. 适配器
  3. 空间分配器 – alloc家族
  4. 迭代器 – 理解为高级指针
  5. 算法库 – 比如sort
  6. 仿函数(函数对象)-- 类重载括号,当成函数去用
  • STL :标准模板库,现在口语化理解为高于 c 的那部分
int a;
类型 名

string str;
类 对象

c++中类生成对象 对应理解为 ==> c中类型生成名

一、STL容器

1.string(非容器)

string str = "ABC"; // 重载=,直接赋值
str.size() / str.length() 长度
str.find() 查找
str.substr() 子串
str.insert() 插入,insert就很特殊
str.replace() 替换

str.find(找什么, 从哪开始)

1.找字符或字符串
str.find('A') 找字符,返回第一个 'A'出现的下标
str.find("BC") 找字符串

2.第二个参数不传,默认从0开始找

3.返回值
str.find()返回值类型size_t,可以隐式转换为int
没找到返回string::npos,npos即no position (int类型值为-1)
利用npos进行判断:
if(str.find('A') == string::npos) cout << "no find";

4.时间复杂度
find时间复杂度O(n*m),不使用额外空间
KMP时间复杂度O(n+m),使用额外空间
str.substr(从哪开始, 长度)

1.substr 子串
2.第二个参数不传,默认截取到字符串结束
str.substr(1, 2) == "BC"
str.insert(从哪开始,插入什么)
str.insert(2, "DF") == "ABDFC"
str.replace(从哪开始, 长度, 替换成什么)
str.repalce(1, 1, "abc") 结果是"AabcC"
getline(从哪获得,存到哪里) -- 读一行 包含空格 的字符串

getline(cin, str)
输入:hello world  (含空格字符串)
输出str:hello world

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

int main() {
    string str = "ABCDEF";
    cout << str.size() << endl; // 6
    cout << str.find("DE") << endl; // 3
    cout << (int)str.find("DE", 5) << endl; // -1 : (int)string::npos
    cout << str.substr(3, 2) << endl; // "DE"
    str.insert(1, "abc"); // "AabCBCDEF"
    cout << str << endl;
    str.replace(2, 5, "000"); // "Aa000EF"
    cout << str << endl;
    getline(cin, str); // 读入含空格字符串
    cout << str << endl;
    return 0;
}

2. vector(容器)

  1. vector矢量(有大小和方向),简单理解为动态数组
vector<int> v;
v.size() 大小
v.push_back(x) 最后插入x,很特殊,目前学的只有vector
v.capacity() 求当前容量能够容纳的元素个数
支持[]访问
  1. 初始化
vector<int> v; //不初始化
vector<int> v(10, 88); //初始化元素个数和值

vector类型,int动态数组里存储的值的类型,v名
v(初始化元素数量, 初始化值)
  1. 扩容
vector<int> v;
vector 向量,理解为动态数组,支持扩容

数组内存空间连续,push_back()数组没有空间会触发扩容
编译器会申请一片新的连续空间,大小取决于编译器
g++/clong申请原空间2倍 vs申请原空间1.5倍

通过v.capacity()查看容器当前已为之分配空间能够容纳的元素数

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

int main() {
    vector<int> v;
    int cnt = -1;
    for (int i = 1; i <= 1000000; i++) {
        if (cnt != v.capacity()) {
            cout << i << ":" << v.capacity() << endl;
            cnt = v.capacity();
        }
        v.push_back(i);
    }
     return 0;
}
  1. 访问数组元素
vector动态数组,支持数组特性,通过[]去访问赋值
前提是[]访问的下标有效,push_back()或初始化
**如使用v[0]、v[1],前提是v[0]、v[1]存在,否则报段错误
  1. 二维动态数组
1.二维数组是套娃
一维里面存的是整型值int
二维里面存的是一维数组vector<int>

2.vector<vector<int> > v(10, vector<int>());
二维数组初始化时,第一个数是个数,第二个数是一维数组
初始化10个空的一维数组

3.vector<vector<int> > v(10, vector<int>(10, 9));
> >中间留空格,因为c++语言特性中>>右移运算符,c++11优化
g++ vector.cpp -std=c++98 不空格会报错
g++ vector.cpp -std=c++11 可以不空格
#include<iostream>
#include <vector>
using namespace std;

int main() {
    vector<vector<int> > v(10, vector<int>(10, 9)); 
    for (int i = 0; i < v.size(); i++) { // v
        for (int j = 0; j < v[i].size(); j++) { // v[i]
            cout << v[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

3. list、forward_list(容器)

双向链表 list
单向链表是 forward_list(向前的)

4. deque(容器)

  1. vector、deque是顺序容器,支持随机访问
  2. deque – double-ended queue,双端队列,可以从两边进出
  3. deque不会触发扩容操作,但是也支持随机访问,原因是 deque本体是一块连续空间存储的一堆指针,每个指针链接一片内存
deque<int> que;
que.push_front(x) 从头入队
que.push_back(x) 从尾入队
que.pop_front()
que.pop_back()
que.front()
que.back()
que.empty()
#include<iostream>
#include <deque>
using namespace std;

int main() {
    deque<int> que;
    que.push_back(3);//尾插
    que.push_back(4);
    que.push_back(5);
    que.push_front(6);//头插
    que.push_front(7);
    while (!que.empty()) {
        cout << que.front() << " " << que.back() << endl;
        que.pop_back();//队尾出队
    }
    return 0;
}

5. stack(适配器)

  1. queue和stack的底层是双端队列deque(容器),两者都是对容器的封装,是2个适配器
stack<int> sta;
sta.size()
sta.push(x)
sta.pop()
sta.top() 区别于queue.front()
sta.empty() 栈空返回真
#include<iostream>
#include <string>
#include <stack>//stack
using namespace std;

int main() {
    stack<string> sta;
    sta.push("1234567890");
    sta.push("abc");
    sta.push("909090909");
    sta.push("T");
    cout << sta.size() << endl;
    while (!sta.empty()) {
        cout << sta.top() << endl;
        sta.pop();
    }
    return 0;
}

6. queue(适配器)

queue<int> que;
que.size()
que.push(x)
que.pop()
que.front() 队首元素
que.back() 队尾元素
que.empty()
#include<iostream>
#include <queue>
using namespace std;

int main() {
    queue<int> que;
    que.push(6);
    que.push(7);
    que.push(1);
    cout << que.size() << endl;
    while (!que.empty()) {
        cout << que.front() << " " << que.back() << endl;
        que.pop();
    }
    return 0;
}
#include<iostream>
#include <queue>
using namespace std;

struct node {
    int num, cnt;
};

int main() {
    queue<node> que;//自定义类型使用容器
    //node 两种方式入队
    node a;
    a.x = a.y = 6;
    que.push(a);
    que.push((node){5, 6}); // 初始化方式
    cout << que.size() << endl;
	node temp = que.front();
    cout << temp.num << " " << temp.cnt << endl;   
    while (!que.empty()) {
        cout << que.front().num << " " << que.front().cnt << endl;
        que.pop();
    }
    return 0;
}

7. priority_queue

  1. 头文件是queue(注意!!)
  2. priority_queue模板里<> 有三个参数,默认大顶堆
    1. <什么类型, 容器(用什么装), 比较规则>
    2. 比较规则 greater 变成小顶堆
      priority_queue<int, vector<int>, greater<int> > que;
  3. 优先对列priority_queue底层是堆
priority_queue<node> que; 会报错 没有排序规则
priority_queue<node, vector<node>, func> que;

que.size()
que.push(x)
que.pop()
que.top() 堆顶 默认大顶堆
// 插入弹出O(logn) 访问O(1)
que.empty()
#include <iostream>
#include <queue> //注意头文件
using namespace std;

int main() {
    priority_queue<int> que;
    que.push(45);
    que.push(-4);
    que.push(3);
    que.push(999);
    cout << que.size() << endl;
    while (!que.empty()) {
        cout << que.top() << endl;
        que.pop();
    }
    return 0;
}

#include <iostream>
#include <queue>//注意头文件
using namespace std;

struct node {
    int num, cnt;
	/* 1、运算符重载
    bool operator< (const node &b) const {
        return this->num < b.num; 
    }
    */
};

// 2、仿函数
struct func {
    bool operator() (node a, node b) {
        return a.num < b.num;
    }
};

int main() {
    // priority_queue<node> que; // 1、node定义中重载 <
    priority_queue<node, vector<node>, func> que; // 2、仿函数
    
    que.push((node){1, 2});
    que.push((node){3, 5});
    que.push((node){7, 9});
    cout << que.size() << endl;
    while (!que.empty()) {
        cout << que.top().num << " " << que.top().cnt << endl;
        que.pop();
    }
    return 0;
}
重载小于号:
容器存自定义类型,涉及到比较必须重载小于号(重载>没用)
运算符重载作用: node类型可以用 < 进行比较
第2const 表示不能修改队列属性
仿函数:
struct func {
    bool operator() (node a, node b) { //定义类 重载()
        return a.num < b.num;
    }
};

priority_queue<node, vector<node>, func> que;
定义que时把仿函数写到参数中
参数中有vector<node> 没有引用vector头文件
//猜想queue<node> que;没写剩余2个参数,默认使用vector或编译器帮忙

8. set

  1. set 集合
  2. set 特点:
    1. 无重复元素 ==> 重复插入只保留一个
    2. 元素是有序的 ==> 可以顺序输出,自定义结构要能比较
    3. 插入、查找、删除的时间复杂度O(logn)
  3. 一般编译器使用红黑树实现set,没有规定用什么方法实现,但是时间复杂度必须是O(logn)
#include<iostream>
#include <set>
using namespace std;

struct node {
    int num, cnt;
    //涉及到排序的容器,自定义类型想用都得重载<
    bool operator< (const node &b) const {
        return this->num < b.num;
    }
};

int main() {
    /*
    set<node> s;
    s.insert((node){8, 9});
    s.insert((node){-34, 234});
    // 把集合里所有元素打印出来 涉及到迭代器
    for (auto it = s.begin(); it != s.end(); it++) {
        cout << it->num << " " << it->cnt << endl;
    }
    */
    set<int> s;
    s.insert(3); // 插入
    s.insert(5);
    s.insert(5); // 重复插入,集合中只有1个5
    s.insert(6);
    s.insert(9);
    cout << s.size() << endl;
    if (s.count(5) == 1) cout << "YES" << endl; // 统计元素数量
    else cout << "NO" << endl;
    s.erase(5); //删除
    if (s.count(5) == 1) cout << "YES" << endl;
    else cout << "NO" << endl;
    //auto自动推导类型(c++11),迭代器暂时理解为指针
    for (auto it = s.begin(); it != s.end(); it++) {
        cout << *it << endl;
    }
    return 0;
}

9.map

  1. map 键值对,map里存的是 “对 ” pair<key, value>,根据key进行排序
  2. map与set的关系:完全一致,除了map中存的是pair
  3. set 保存一个属性,map 保存一个属性对应的另一个属性(map比set多了一个维)
  4. set 用于去重,排序
    map 在set 基础上对每个元素出现的位置、次数进行记录,经典应用是统计一篇文章每个单词出现的次数
m.insert(make_pair());
m.count()
m.erase()
[]
#include<iostream>
#include <map>
#include <string>
#include <utility> //make_pair
using namespace std;

int main() {
    // map<key, value> key必须能排序 value是任意的类型
    // 根据键排序, string已将重载好了各种比较符号
    map<string, int> m;
    // 使用make_pair函数生成临时pair,代替插入node
    // pair<int, int> p; 想访问p里的2个元素 p.first p.second
    m.insert(make_pair("12345", 789));
    m.insert(make_pair("abcdefg", 11111));
    m.insert(make_pair("T", 99));
    if (m.count("abcdefg") == 1) {// 统计数量
        cout << "YES" << endl;
        cout << m["abcdefg"] << endl;// []访问
    } else {
        cout << "NO" << endl;
    }

    m["9999"] = 5678;// []插入
    cout << m["9999"] << endl;//5678
    m.erase("9999");
    cout << m.count("9999") << endl;//0
    //使用[]访问时,无论是否存在,都会插入一个
    //m["****"]访问时,map中有"****"返回对应值,无则新建键值对,默认键值0
    //不能使用[]去查询map中有无某个元素,会导致集合中有很多垃圾值
    cout << m["9999"] << endl;//未找到对应string,返回int默认值0
    cout << m.count("9999") << endl;//1
    //用迭代器遍历
    for (auto it = m.begin(); it != m.end(); it++) {
        cout << it->first << " " << it->second << endl;
    }
    return 0;
}

10. multiset / multimap

  1. set / map 中所有元素都是唯一的,multiset / multimap 里面的元素不是唯一的,是多重的,可以插入多次
  2. 用不了 [ ](key有多个,不知道返回哪个)

11. unordered_set / unordered_map

#include<iostream>
#include <unordered_map>//unordered_map
#include <string>
using namespace std;

int main() {
    unordered_map<string, int> m;
    m["1234567890"] = 67890;
    m["(+_+)"] = 66666;
    m["abcdefg"] = 12345;
    cout << m.size() << endl;
    for (auto it = m.begin(); it != m.end(); it++) {
        cout << it->first << " " << it->second << endl;
    }
    return 0;
}
#include<iostream>
#include <unordered_map>//unordered_map头文件,c++11
#include <string>
using namespace std;

struct node {
    int x, y;
    bool operator< (const node &b) const {
        return this->x > b.x;
    }
};

int main() {
    //unordered_map底层是哈希表,对于常见的string类型不用自己实现哈希函数,自定义类型要自己实现哈希函数
    /*
    unordered_map<node, int> m2;//自定义结构必须重载<
    node a;
    m2[a] = 5;
    cout << m2[a] << endl;
    */
    unordered_map<string, int> m;//string键,int值
    string a = "123";
    m[a] = 999;//[]访问
    cout << m["123"] << endl;
    cout << m[a] << endl;
    cout << m["456"] << endl;//未找到对应string,返回int默认值0
    return 0;
}
#include<iostream>
#include <set>
#include <unordered_set>
using namespace std;

//数据不要求有序,用unordered_set/map更高效

int main() {
    set<int> s1;
    unordered_set<int> s2;
    for (int i = 0; i < 1000000; i++) {
        s1.insert(i);//单独运行时间1.2s 插入O(logn)
        s2.insert(i);//单独运行时间0.4s 插入O(1)
    }
    //插入一共花费时间 1.6s

    for (int i = 0; i < 1000000; i++) {
        s1.count(i);//单独查找0.5s
        s2.count(i);//单独查找0.1s
    }
    return 0;
}
  1. JAVA中有hash_map,用哈希表实现了map,具有2个特点无序,增删改查时间复杂度o(1)

  2. C++11后实现了unordered_set,本质也是哈希表,由于不需要有序,性能更高
    unordered_set
    unordered_map
    unordered_multiset
    unordered_multumap
    以上都是用哈希表实现的

  3. 操作与set / map 一样

  4. 无序 map 底层是哈希表,数据存到哈希表里要经过哈希函数的运算( O(1) ),常用结构的哈希函数已经在c++中写好了,自定义类型用无序map要自己实现哈希函数

二、题目

383.周末舞会

在这里插入图片描述


378.字符串括号匹配2

在这里插入图片描述


376.机器翻译

在这里插入图片描述
在这里插入图片描述


379.仓库日志

在这里插入图片描述

在这里插入图片描述


382.报数

在这里插入图片描述


384.敲七

在这里插入图片描述


385.海港

在这里插入图片描述
在这里插入图片描述


le232. 用栈实现队列

定义2个栈s1, s2, 把s2当作中转的栈,实现队列

le225. 用队列实现栈

定义一个队列,push(x)后通过for循环把插入前的数据放到x后面,这样就模拟了一个栈

深搜的讲解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值