蓝桥杯C/C++5天逆袭省一之第二天——C++STL容器(基础版)

一、回顾与引入

在第一天我们了解了蓝桥杯赛制以及暴力解法在比赛中的应用。暴力解法能帮我们在一些题目中拿到部分分数,但想要在比赛中取得更好成绩,还需要掌握更高效的工具和方法。今天我们就来学习 C++ 中非常实用的 STL(Standard Template Library,标准模板库)容器,这一节的基础版本 我会重点讲解常用的:vector、queue、set、stack 这四个容器。这些容器能帮助我们更方便、高效地处理数据,在蓝桥杯的编程题中经常会用到。

注意:本小结不进行真题讲解,因为都是容器的基本用法,大家学会后在之后的做题中慢慢用这些容器去代替数组的用法,stack和queue明天在学习bfs和dfs的时候会用到。

二、vector 容器

(一)概念

在 C++ 里,vector 是标准模板库(STL)中的一个动态数组容器,它能在运行时按需调整大小。可以把它看作是一种增强版的数组,存储同一类型的多个元素。

(二)特点

  1. 动态大小vector 可在运行时动态调整大小,这意味着你能在程序运行时根据需要添加或删除元素,不用像普通数组那样提前确定大小。
  2. 连续存储:元素在内存中是连续存储的,这样就能像普通数组一样通过下标快速访问元素,时间复杂度为O(1)。
  3. 自动内存管理vector 会自动处理内存的分配和释放,降低了因手动管理内存而产生错误的风险。

(三)定义与初始化

#include <bits/stdc++.h>
using namespace std;
int main() {
    // 定义一个存储 int 类型的空 vector
    vector<int> v1; 
    //定义一个存储int类型且长度为100的vector动态数组,与int a[100]等价使用
	vector<int>v2(100); 
    // 定义一个存储 int 类型且初始大小为 5 的 vector,每个元素初始值为 0
    vector<int> v3(5, 0); 
    // 通过初始化列表初始化 vector
    vector<int> v4 = {1, 2, 3}; 
    //复制一个与v4一样的动态数组v5
	vector<int>v5 (v3);
	//如果要得到v4的逆序数组,用反向迭代器 
	vector<int>v6(v3.rbegin(),v3.rend()); 
	//是将 v7 初始化为一个二维向量,该向量有 n 行 m 列,且所有元素初始值都为 0。
	//与int a[n][m]等价使用
	vector<vector<int>>v7(n,vector<int>(m,0));
    return 0;
}

(四)迭代器(iterators)

begin():返回指向容器中的第一个元素

end():返回指向容器中最后一个元素的后一个位置的迭代器

rbegin():返回容器逆序的第一个元素的反向迭代器

rend():返回容器逆序的最后一个元素的前一个位置的迭代器

(考虑到概念晦涩难懂,下图是我对迭代器的个人理解)

(五)输入输出

#include<bits/stdc++.h>
using namespace std;
int main()
{
	vector<int>a(10);
	//1 2 3 4 5 6 7 8 9 10
	for(int i=0;i<10;i++)cin>>a[i];
	//正向输出方法1:
	for(int i=0;i<10;i++)cout<<a[i]<<" ";
	cout<<endl;
	//正向输出方法2:
	for(vector<int>::iterator it=a.begin();it!=a.end();it++)cout<<*it<<" ";
	cout<<endl;
	//正向输出方法3:
	for(auto num:a)cout<<num<<" "; 
	cout<<endl; 
	//反向输出方法1:
	for(int i=9;i>=0;i--)cout<<a[i]<<" ";
	cout<<endl;
	//反向输出方法2:
	for(vector<int>::reverse_iterator it=a.rbegin();it!=a.rend();it++)cout<<*it<<" ";
	cout<<endl;
}

注意:正向输出方法3非常方便简洁,建议大家掌握,不过这句话是c++11版本新加的,有些同学使用的devc默认使用环境中还没有这种语法,大家可以点击上方工具——>编译选项,加上以下语句,重新运行就好了。

(六)常用函数

push_back():用于在 vector 的末尾添加一个元素。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	vector<int>a;
	for(int i=1;i<=10;i++)a.push_back(i);
	for(auto num:a)cout<<num<<" ";
	//1 2 3 4 5 6 7 8 9 10
}

 pop_back():用于删除 vector 的最后一个元素。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	vector<int>a={1,2,3,4,5,6,7,8,9,10};
	a.pop_back();
	for(auto num:a)cout<<num<<" ";
	//1 2 3 4 5 6 7 8 9
}

 size():返回 vector 中元素的数量。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	vector<int>a={1,2,3,4,5,6,7,8,9,10};
	cout<<a.size();
	//10
}

clear():清空 vector 中的所有元素。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	vector<int>a={1,2,3,4,5,6,7,8,9,10};
	a.clear();
	cout<<a.size();
	//0
}

empty():检查vector是否为空

#include<bits/stdc++.h>
using namespace std;
int main()
{
	vector<int>a={1,2,3,4,5,6,7,8,9,10};
	a.clear();
	cout<<a.empty();
	//1(输出1相当于true,为空;如果输出0相当于false)
}

(七)习题训练

题目描述:

给定一个未排序的整数数组 nums 和一个整数 k,找出数组中第 k 大的元素。

思路讲解:

这道题的核心思路是对数组进行排序,然后根据排序后的结果获取第 k 大的元素。

由于我们要找第 k 大的元素,所以可以选择从大到小的排序方式,这样第 k 个位置(索引为 k - 1)的元素就是我们要找的结果。

在 C++ 中,sort 函数默认是从小到大排序,我们可以通过传入自定义比较函数对象,来实现从大到小的排序。

代码实现:

#include <bits/stdc++.h>
using namespace std;
//自定义排序规则
bool cmp(int a,int b)
{
	return a>b;	
} 
// 找出数组中第 k 大的元素
int findKthLargest(vector<int> nums, int k) {
    // 对数组进行排序,从大到小排列
    sort(nums.begin(), nums.end(), cmp);
    // 第 k 大的元素索引为 k - 1
    return nums[k - 1];
}

int main() {
    vector<int> nums = {3, 2, 1, 5, 6, 4};
    int k = 2;
    // 调用函数找出第 k 大的元素
    int result = findKthLargest(nums, k);
    cout << "第 " << k << " 大的元素是: " << result << endl;
    return 0;
}

(八)在蓝桥杯中的应用场景

在处理需要动态存储一组同类型数据,且数据量不确定的题目时很有用。比如统计一系列学生的成绩,事先不知道有多少学生,就可以用 vector 来存储成绩。

三、set容器

(一)概念

在 C++ 里,set 是标准模板库(STL)中的一个关联容器,它以红黑树(一种自平衡的二叉搜索树)作为底层数据结构,用于存储唯一的元素。也就是说,set 中不会存在重复的元素。

(二)特点

  1. 元素唯一性set 中的每个元素都是唯一的,插入重复元素时会被忽略。
  2. 自动排序set 会自动根据元素的值对元素进行排序,默认是升序排列。
  3. 高效查找:由于采用红黑树作为底层结构,查找、插入和删除操作的时间复杂度都是 O(log n)。

(三)定义与初始化

#include <bits/stdc++.h>
using namespace std;
int main() {
	
    set<int> s; // 定义一个存储 int 类型的空 set
    set<int> s = {3, 1, 2}; // 使用初始化列表初始化 set,会自动排序为 1, 2, 3
    return 0;
}

(四)常用函数

insert():用于向 set 中插入元素。

#include <bits/stdc++.h>
using namespace std; 
int main() {
    set<int> s;
    s.insert(10);
    return 0;
}

erase():用于删除 set 中的元素。

#include <bits/stdc++.h>
using namespace std; 
int main() {
	set<int> s = {1, 2, 3};
    s.erase(2);
    return 0;
}

find():用于查找 set 中是否存在某个元素,若存在则返回指向该元素的迭代器,否则返回 end() 迭代器。

#include <bits/stdc++.h>
using namespace std; 
int main() {
    set<int> s = {1, 2, 3};
    auto it = s.find(2);
    if (it != s.end()) {
        cout << "Element found: " << *it << endl;
    } else {
        cout << "Element not found" <<endl;
    }
    return 0;
}

size():返回 set 中元素的数量。

#include <bits/stdc++.h>
using namespace std; 
int main() {
    set<int> s = {1, 2, 3};
    cout << "Set size: " << s.size() <<endl;
    return 0;
}

(五)排序规则(重点!!!)

对于 int 类型的 set,默认是按照元素的值从小到大进行排序。

#include <bits/stdc++.h>
using namespace std;
int main() {
    set<int> s = {3, 1, 2};
    for(auto num:s)cout<<num<<" ";
    //1 2 3
    cout << endl;
    return 0;
}

也可以自己来制定排序规则

#include <bits/stdc++.h>
using namespace std;
class cmp{
	public:
		bool operator()(const int& a,const int& b){
			return a>b;//从大到小 
			//return a<b;//从小到大 
		}
};
int main() {
    set<int, cmp> s = {3, 1, 2};
    for(auto num:s)cout<<num<<" ";
    cout <<endl;
    return 0;
}

结构体类型也可以排序:(重点,重点,重点!!)

#include<bits/stdc++.h>
using namespace std;
struct Student{
    int id;
    int score;
    Student(int i, int s) : id(i), score(s) {}//可以直接使用Student(i,s)插入到set中 
};

class cmp{
	public:
		bool operator()(const Student& a,const Student& b){
			return a.score>b.score;//按分数从高到低 
		}
};

int main() {
    set<Student, cmp> s;
    s.insert(Student(1, 80));
    s.insert(Student(2, 90));
    s.insert(Student(3, 70));
    for(auto num:s)cout<< "ID: " << num.id << ", Score: " << num.score << endl;
    return 0;
}

(六)习题训练

题目描述:

给定一个包含重复元素的整数数组,要求使用 set 容器删除数组中的重复元素,并将剩余元素按升序排序,最后输出处理后的元素。

思路讲解:

set 容器的特性是元素唯一且会自动按升序排序,所以可以利用这个特性来解决问题。

遍历给定的整数数组,将数组中的每个元素插入到 set 中。由于 set 会自动处理重复元素,重复插入相同元素时会被忽略。

插入完成后,set 中就存储了原数组中所有不重复且按升序排列的元素。

最后遍历 set 并输出其中的元素。

代码实现:

#include <bits/stdc++.h>
using namespace std;

int main() {
    // 给定包含重复元素的数组
    vector<int> nums = {3, 1, 2, 2, 4, 3};
    // 定义一个 set 容器
    set<int> uniqueNums;

    // 遍历数组,将元素插入 set
    for (int num : nums) {
        uniqueNums.insert(num);
    }

    // 输出 set 中的元素
    for (int num : uniqueNums) {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

(七)在蓝桥杯中的应用场景

当需要处理去重或者对元素进行排序存储的情况时很实用。比如统计一篇文章中出现过的不同单词,使用 set 可以自动去重并排序。

四、队列(queue)容器

(一)概念

在 C++ 里,queue 是标准模板库(STL)中的一个容器适配器,它遵循先进先出(FIFO,First In First Out)的原则。这和现实生活中的排队情况类似,先到的人先接受服务,先进入队列的元素也会先被处理。

(二)特点

  1. FIFO 原则:元素的插入和删除操作遵循先进先出的顺序,插入操作在队尾进行,删除操作在队首进行。
  2. 封装性queue 是对其他容器(如 deque 或 list)进行封装,提供了特定的接口,隐藏了底层容器的实现细节。
  3. 操作受限:只能在队首删除元素,在队尾插入元素,不支持随机访问和在中间插入或删除元素。

(三)定义及初始化

#include <bits/stdc++.h>
using namespace std;

int main() {
    // 定义一个存储 int 类型的空队列
    queue<int> q;

    return 0;
}

(四)常用函数

push():用于在队列的尾部插入一个元素。

#include <bits/stdc++.h>
using namespace std;

int main() {
    queue<int> q;
    q.push(10);
    return 0;
}

pop():用于删除队列头部的元素。注意,该函数不返回被删除的元素。

#include <bits/stdc++.h>
using namespace std;

int main() {
    queue<int> q;
    q.push(10);
    q.pop();
    return 0;
}

front():返回队列头部的元素,但不删除该元素:

#include <bits/stdc++.h>
using namespace std;

int main() {
    queue<int> q;
    q.push(10);
    int frontElement = q.front();
    cout << "队首元素: " << frontElement << endl;
    return 0;
}

back():返回队列尾部的元素,但不删除该元素。

#include <bits/stdc++.h>
using namespace std;

int main() {
    queue<int> q;
    q.push(10);
    q.push(20);
    int backElement = q.back();
    cout << "队尾元素: " << backElement << endl;
    return 0;
}

empty():判断队列是否为空,如果队列为空则返回 true,否则返回 false

#include <bits/stdc++.h>
using namespace std;

int main() {
    queue<int> q;
    if (q.empty()) {
        cout << "队列为空" << endl;
    }
    return 0;
}

size():返回队列中元素的数量。

#include <bits/stdc++.h>
using namespace std;

int main() {
    queue<int> q;
    q.push(10);
    q.push(20);
    int queueSize = q.size();
    cout << "队列元素数量: " << queueSize << endl;
    return 0;
}

(五)输入输出

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n;
    cout << "请输入元素数量: ";
    //5
    cin >> n;
    queue<int> q;
    //1 2 3 4 5
    for (int i = 0; i < n; ++i) {
        int num;
        cin >> num;
        q.push(num);
    }
    //1 2 3 4 5
     while (!q.empty()) {
        cout << q.front() << " ";
        q.pop();
    }
    cout << endl;
    return 0;
}

(六)习题训练

题目描述:

有 n 个人围成一圈,从第一个人开始报数,报到 m 的人出列,然后从出列的下一个人重新开始报数,报到 m 的人又出列,如此反复,直到所有人都出列,求最后剩下的人的编号。

思路讲解:

可以使用队列来模拟这个过程。首先将 1 到 n 的编号依次加入队列。

然后开始循环,每次从队列头部取出一个元素,若该元素不是第 m 个报数的,就将其重新加入队列尾部;若该元素是第 m 个报数的,则将其从队列中移除。

不断重复这个过程,直到队列中只剩下一个元素,这个元素就是最后剩下的人的编号。

代码实现:

#include <bits/stdc++.h>
using namespace std;

// 解决约瑟夫环问题
int josephus(int n, int m) {
    // 定义队列
    queue<int> q;
    // 将 1 到 n 的编号依次加入队列
    for (int i = 1; i <= n; i++) {
        q.push(i);
    }

    while (q.size() > 1) {
        // 报数 m - 1 次
        for (int i = 1; i < m; i++) {
            // 将队首元素取出并加入队尾
            int front = q.front();
            q.pop();
            q.push(front);
        }
        // 第 m 个报数的人出列
        q.pop();
    }

    // 返回最后剩下的人的编号
    return q.front();
}

int main() {
    int n = 5; // 总人数
    int m = 3; // 报数到 m 的人出列
    // 调用函数求解
    int result = josephus(n, m);
    cout << "最后剩下的人的编号是: " << result << endl;
    return 0;
}

(七)在蓝桥杯中的应用场景

常用于广度优先搜索(BFS)算法中。比如在走迷宫问题里,用来存储每一步可以到达的位置,按照进入的先后顺序依次处理,从而找到从起点到终点的最短路径。

五、堆栈(stack)容器

(一)概念

在 C++ 里,stack 是标准模板库(STL)中的一个容器适配器,它遵循后进先出(LIFO,Last In First Out)的原则。可以把它想象成一摞盘子,最后放上去的盘子会最先被拿走,在 stack 中,最后插入的元素会最先被删除。

(二)特点

  1. LIFO 原则:这是 stack 最核心的特点,元素的插入(入栈)和删除(出栈)操作都在栈顶进行,保证了后进入的元素先出来。
  2. 封装性stack 基于其他容器(如 dequevector 或 list)实现,它封装了底层容器的操作,只提供了与栈相关的接口,如 pushpop 等。
  3. 操作受限:只能对栈顶元素进行操作,不支持随机访问,也不能在栈的中间插入或删除元素。

(三)定义及初始化

#include <bits/stdc++.h>
using namespace std;

int main() {
    // 定义一个存储 int 类型的空栈
    stack<int> s;

    return 0;
}

(四)常用函数

push():将一个元素压入栈顶。

#include <bits/stdc++.h>
using namespace std;

int main() {
    stack<int> s;
    s.push(10);
    return 0;
}

pop():移除栈顶元素,但不返回该元素。

#include <bits/stdc++.h>
using namespace std;

int main() {
    stack<int> s;
    s.push(10);
    s.pop();
    return 0;
}

top():返回栈顶元素的引用,但不将其从栈中移除。

#include <bits/stdc++.h>
using namespace std;

int main() {
    stack<int> s;
    s.push(10);
    int topElement = s.top();
    cout << "栈顶元素: " << topElement << endl;
    return 0;
}

empty():判断栈是否为空,若为空则返回 true,否则返回 false

#include <bits/stdc++.h>
using namespace std;

int main() {
    stack<int> s;
    if (s.empty()) {
        cout << "栈为空" << endl;
    }
    return 0;
}

size():返回栈中元素的数量。

#include <bits/stdc++.h>
using namespace std;

int main() {
    stack<int> s;
    s.push(10);
    s.push(20);
    int stackSize = s.size();
    cout << "栈中元素数量: " << stackSize << endl;
    return 0;
}

(五)输入输出

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    cout << "请输入元素数量: ";
    //5
    cin >> n;
    stack<int> s;
    //1 2 3 4 5
    for (int i = 0; i < n; ++i) {
        int num;
        cin >> num;
        s.push(num);
    }
    //5 4 3 2 1
    while (!s.empty()) {
        cout << s.top() << " ";
        s.pop();
    }
    cout << endl;
    return 0;
}

(六)习题训练

题目描述:

给定一个字符串,使用栈来反转该字符串。

思路讲解:

利用栈后进先出的特性,将字符串的每个字符依次压入栈中。

然后依次从栈中弹出字符,组成新的字符串,这样就实现了字符串的反转。

代码实现:

#include <bits/stdc++.h>
using namespace std;

// 反转字符串
string reverseString(string s) {
    // 定义栈
    stack<char> st;
    // 将字符串的每个字符依次压入栈中
    for (char c : s) {
        st.push(c);
    }

    string reversed = "";
    // 依次从栈中弹出字符,组成新的字符串
    while (!st.empty()) {
        reversed += st.top();
        st.pop();
    }

    return reversed;
}

int main() {
    string s = "hello";
    // 调用函数反转字符串
    string reversed = reverseString(s);
    cout << "反转后的字符串是: " << reversed << endl;
    return 0;
}

(七)在蓝桥杯中的应用场景

常用于深度优先搜索(DFS)算法中,也可用于表达式求值等场景。比如在计算后缀表达式的值时,可以利用栈来存储操作数,按照运算规则进行计算。

六、总结

今天我们学习了 vector、queue、set、stack 这几种 STL 容器的基本概念、操作以及在蓝桥杯比赛中的应用场景。熟练掌握这些容器,能让我们在处理数据时更加得心应手,提高编程效率。在后续的学习中,我们还会深入探讨更多关于 STL 以及其他有助于在蓝桥杯中取得好成绩的知识内容。 明天我们将学习 BFS(广度优先搜索)和 DFS(深度优先搜索)算法,这也是在算法竞赛中非常重要的解题方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值