算法基础(三)——STL容器的简单使用

§ STL

  • 标准模板库,日常简单理解为标准库

  • 这三个字母在C++标准文档中没有出现,是人为制造出来的东西。于是STL的概念就没那么清楚、比较模糊,比如string是标准库里的,但不是标准模板库里的。因此string是不是STL里的不好说

  • 不用纠结定义,就当奇闻轶事乐趣

  • 6大组件
    ①容器
    ②迭代器:理解成指针
    ③适配器:容器进行二次封装得到的
    ④算法:如sort、lower_bound
    ⑤仿函数:C++特性,重载了()使得具有函数功能
    ⑥分配器:理解为C语言的malloc、calloc

1. string

  • 头文件include <string>string s;对应类 对象,如同int a对应类型 名
  • 获取字符串长度
s.size();
s.length();
//返回值是 size_type,与int比较时得强转类型
//两个函数没有任何区别

错误想法:将字符串中的某一个字符改成字符串结束标志,希望把字符串给截掉

string s = "123";
s[1] = '\0';
cout << s.size();
  • 查找
s.find("abc");
//s.find(查找什么, 下标从ind开始往后找而省略时默认为0)
//返回值也是size_type
//没找到返回值string::npos(no position 的缩写)
//没找到的返回值表现为unsigned long long的极大值即long long -1

//判断是否找到
if (s.find(...) == string::npos)
//将string::nops改成-1不太严谨
  • 截取
s.substr(ind, 长度)
//ind指的是从下标为ind的位置开始截取
s.substr(ind);
//从ind开始截取到末尾
  • 替换
s.replace(ind, 被替换字符串的长度, 替换成什么);
//删除,替换成空字符串即可
  • 插入
s.insert(ind, 插入什么);

以上只是常见用法,在算法做题中够用,其它重载的功能可以在cppreference.com中查看

2. vector

  • 向量,长度不定的动态数组。头文件#include <vector>,类型定义vector<类型> 变量名

  • 判空v.empty(),一般不怎么用,一般用下面的

  • 返回元素数量v.size()

  • 在vector的最后加入元素xv.push_back(x)

  • 清空v.clear()
    以上只是写算法题时常用的,在指定位置插入元素略过,因为要使用迭代器

  • 初始化与遍历

通过下标访问

//初始化10个int元素,默认0
vector<int> v1(10);
//访问下标为3的元素
//访问跟数组访问的方式一样
cout << v1[3] << endl;
//初始化10个9
vector<int> v2(10, 9);
//二维数组
//下面的两个>分开是因为C++11标准前的编译器会把>>当成右移运算符
vector<vector<int> > v3(5, vector<int>(6, 10));
//初始化5行的一维数组,每个一维数组存放6个元素10
//列表初始化、遍历 C++11特性
vector<int> v = {1, 2, 3, 4};
//auto自动推导类型
for (auto x : v) {
	cout << x << endl;
}
  • vector有限空间+动态扩容
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v;
    int t = 0;
    for (int i = 0; i < 100000; i++) {
        v.push_back(1);
        if (t != v.capacity()) {
            t = v.capacity();
            cout << t << endl;
        }
    }

    return 0;
}

扩容规则取决于环境、编译器
在这里插入图片描述

  • 时间复杂度
    在这里插入图片描述
    来源:cppreference

3. stack

  • 栈,stack<int> sta;
  • 判空sta.empty()
  • 元素数量sta.size()
  • 入栈sta.push(x)

下面两个操作得保证栈非空

  • 出栈sta.pop()
  • 访问栈顶元素sta.top()

栈是用什么实现的?stack<int, 模板参>默认使用双端队列deque(队首队尾都可以入队出队),实现栈只需要关闭一些双端队列的功能。那么双端队列经过二次封装得到了栈这个容器适配器

4. queue

  • queue<int> que,跟stack不能说一模一样,只能说是完全一致
  • 判空
  • 元素数量
  • que.push(x),入队尾;que.pop(),队首出
  • 访问队首元素que.front();访问队尾que.back()

演示:玩一个花的。只要是类型已知,都可以存,同理也可以存储自定义类型

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

int main() {
    int num[10] = {1, 2, 3, 4, 5};
    queue<int *> que;
    que.push(&num[2]);
    que.push(&num[4]);
    while (!que.empty()) {
        int *p = que.front();
        que.pop();
        cout << *p << endl;
    }
    return 0;
}

在这里插入图片描述

自定义类型

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

struct node {
    int x, y;
};

int main() {
    queue<node> que;
    que.push((node){4, 6});
    //que.push({4, 6});
    que.push({0, 9});
    que.push({5, -1});
    que.push({0, 0});
    cout << "元素数量:" << que.size() << endl;
    while (!que.empty()) {
        node temp = que.front();
        que.pop();
        cout << temp.x << " " << temp.y << endl;
    }

    return 0;
}

在这里插入图片描述

  • 实现方式和栈一样,是一个容器适配器

5. deque

  • 双端队列,顺序容器,支持随机访问即可以通过[]直接访问
    基本操作

  • 判空、获得数量

  • 插入:队尾que.push_back(x),队首que.push_front(x)

  • 删除:队尾que.pop_back(),队首que.pop_front()

  • 访问队首队尾元素:que.back()que.front()

  • 问题:以上操作时间复杂度都是常数级别O(1),随机访问不是应该内存连续?双端队列底层实现逻辑:内存不连续,二级指针,通过偏移量访问

在这里插入图片描述

6. priority_queue

  • 优先队列,和queue共用一个头文件
  • 默认是大顶堆
  • 操作和队列一样,除了访问元素。访问堆顶元素:que.top()
  • 用小顶堆,用到第二个第三个模板参:priority_queue<类型, 用什么容器存储, 排序方法>,其中“排序方法”默认是less<int>,与之对应的就是greater<int>表示小顶堆
  • C++中还有一个make_heap函数利用数组建堆,得到优先队列
  • 自定义类型
#include <iostream>
#include <queue>
using namespace std;

struct node {
    int x, y;
    //跟sort一样必须指定排序方法
    //堆里面的重载逻辑反着来
    bool operator<(const node &b) const {
    	//<号,大顶堆
        return this->x < b.x;
    }
};

//方法二仿函数:重载括号的类
struct cmp {
	bool operator() (const node &a, const node &b) const {
		return a.x < b.x;
	}
};

int main() {
    priority_queue<node> que;
    //priority_queue<node, vector<int>, cmp> que;
    que.push({5, 6});
    que.push({10, 1});
    que.push({7, 9});
    que.push({-1, 2});
    que.push({6, 8});
    while (!que.empty()) {
        node temp = que.top();
        que.pop();
        cout << temp.x << " " << temp.y << endl;
    }
    return 0;
}

在这里插入图片描述

7. set

  • 集合,有序、去重,默认递增,自定义类型得指定排序规则
  • 底层实现:现在能见到的的编译器都用红黑树实现,因为红黑树比较合适。但是标准没有定义,只规定插删等操作是平均对数级别限制复杂度
  • 判空、返回集合元素数量
  • 插入s.insert(x)
  • 删除s.erase(x)
  • 查找s.count(x)返回x的数量(要么是0要么是1),s.find(x)返回的是指针/迭代器

代码演示1

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

int main() {
    set<int> s;
    s.insert(5);
    s.insert(8);
    s.insert(2);
    s.insert(-1);
    s.insert(0);
    cout << s.size() << endl;
    if (s.count(5) == 1) {
        cout << "5 yes" << endl;
    } else {
        cout << "5 no" << endl;
    }
    s.erase(5);
    if (s.count(5) == 0) {
        cout << "5 no" << endl;
    }
    for (auto it = s.begin(); it != s.end(); it++) {
        cout << (*it) << " ";
    }
    /*
    for (auto x : s) {
    	cout << x << " ";
    }

	//反向迭代器
	for (auto it = s.rbegin(); it != s.rend(); it++) {
        cout << (*it) << " ";
    }
    */
    cout << endl;
    return 0;
}

结果
在这里插入图片描述

代码演示2:自定义类型指定排序方法

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

struct node {
    int x, y;
    bool operator<(const node &a) const {
        return x + y > a.x + a.y;
    }
};

int main() {
    set<node> s;
    s.insert({5, 0});
    s.insert({7, 2});
    s.insert({4, 9});
    s.insert({11, -1});
    cout << s.size() << endl;
    for (auto it = s.begin(); it != s.end(); it++) {
        cout << it->x << " " << it->y << "\tsum = " << it->x + it->y << endl;
        //it当成指针使用
    }
    return 0;
}

结果
在这里插入图片描述

8. map

  • 映射,唯一关系、键值对存储;有序(根据键排序)去重,map的升级版
  • map<int, string> m,键值对pair<key, value>将两个元素糅在了一起(key→first,value→second),而map中存储的正是这种键值对。键key是唯一的
  • 判空、元素数量
  • 插入m.insert(pair)
  • 删除m.erase(key)删除键即可
  • 查找m.count(key)m.find(key)
  • map重载的中括号[],直接用m[key]修改或插入,因此通常都使用中括号插入元素

代码演示

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

int main() {
    map<string, int> m;
    cout << "map size = " << m.size() << endl;
    m.insert(make_pair("abc", 99));
    m["abcd"] = 10;
    m["+-+-+"] = 12;
    m["324"] = 22;
    cout << "map size = " << m.size() << endl;
    if (m.count("abcd") == 1) {
        cout << "abcd yes, value = " << m["abcd"] << endl;
    }
    m.erase("324");
    cout << "map size = " << m.size() << endl;
    for (auto it = m.begin(); it != m.end(); it++) {
        cout << it->first << ", " << it->second << endl;
    }


	/*
	if (m["ddddd"]) {
		cout << "yes" << endl;
	}
	*/
	//功能上没问题,但是这里会创建一个键为"ddddd"的键值对,int的默认值是0
    return 0;
}

结果
在这里插入图片描述

9. C++11后的容器

unordered_set和unordered_map

  • 用hash实现的集合和映射,简单的用法与set、map完全一样
  • 头文件分别为自己的名字
#include<set>#include<map>
有序去重setmap
有序不去重multisetmultimap 中括号访问的是任意的,一般不用
#include<unordered_set>#include<unordered_map>
无序去重unordered_setunordered_map
#include<unordered_multiset>#include<unordered_multimap>
无序不去重unordered_multisetunordered_multimap
  • 用自定义类型的unordered_map需要重载hash函数与==运算符

刷题

1.字符串括号匹配

378
在这里插入图片描述
样例输入1

a(cc[])bbb()@

样例输出1

YES

样例输入2

a(cc[)]bbb()@

样例输出2

NO

在这里插入图片描述
分析:

  1. 只需要把括号入栈,左括号全入栈,右括号匹配出栈
  2. 如果不匹配直接输出
  3. 匹配完若栈不为空说明还有多余的左括号,不匹配
#include <iostream>
#include <string>
#include <stack>
using namespace std;

int main() {
    stack<char> sta;
    string s;
    cin >> s;
    int f = 0; // 1不匹配 0匹配
    for (auto c : s) {
        if (c == '(' || c == '[' || c == '{') {
            sta.push(c);
        } else if (c == ')') {
            if (!sta.empty() && sta.top() == '(') {
                sta.pop();
            } else {
                f = 1;
                break;
            }
        } else if (c == ']') {
            if (!sta.empty() && sta.top() == '[') {
                sta.pop();
            } else {
                f = 1;
                break;
            }
        } else if (c == '}') {
            if (!sta.empty() && sta.top() == '{') {
                sta.pop();
            } else {
                f = 1;
                break;
            }
        }
    }
    if (!sta.empty()) f = 1;
    if (f == 1) cout << "NO" << endl;
    else cout << "YES" << endl;
    return 0;
}

2.仓库日志

379
在这里插入图片描述
样例输入

13
0 1
0 2
2
0 4
0 2
2
1
2
1
1
2
1
2

样例输出

2
4
4
1
0

在这里插入图片描述
分析:建立一个辅助栈,存储最大值,每当存储的货物数量小于等于最大值栈的top元素值,则将此最大值再入一遍;若大于最大值,则入当前值。当然最开始先把0压栈。出栈时一同出栈即可(货物栈可以不需要)。

3.编辑

482
在这里插入图片描述
样例输入

dmih
11
B
B
P x
L
B
B
B
P y
D
D
P z

样例输出

yxz

在这里插入图片描述
分析:光标左右边分别建立栈

4.敲七

384
在这里插入图片描述
样例输入

4 3 6

样例输出

3

在这里插入图片描述
分析

  • 约瑟夫环问题,但这里用数学公式有点麻烦
  • 用队列模拟,判断队首,若安全则拿到队尾;否则直接出队
  • 当队列元素为1时直接输出

5.查字典

575
在这里插入图片描述
样例输入

2
scan
10
word
15
2
scan
word

样例输出

10
15

在这里插入图片描述
分析

  • unordered_map快一些

6.溶液模拟器

569
在这里插入图片描述
样例输入

100 100
2
P 100 0
Z

样例输出

200 50.00000
100 100.00000

分析

  • 溶液质量=溶剂+溶质(盐水=盐+水);浓度=溶质/溶液(盐/盐水)
  • 有撤销操作肯定需要栈,存储每步加入的盐水的信息(自定义类型)
  • 自定义结构
struct node {
	double v, c, salt;//体积、浓度、盐的质量
	//salt = v * c / 100; //c的符号是%
};

7.上网统计

566
在这里插入图片描述
样例输入

5 7
goodstudyer bookshopa
likespacea spaceweb
goodstudyer bookshopb
likespaceb spaceweb
likespacec spaceweb
likespacea juiceshop
gameplayer gameweb

样例输出

goodstudyer bookshopa bookshopb
likespacea spaceweb juiceshop
likespaceb spaceweb
likespacec spaceweb
gameplayer gameweb

分析

  • 可以用map,用户 映射 下标
  • vector二维数组,每个一维数组存储下标对应的网页地址
  • 做法不唯一

其它题目573(用优先队列)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值