算法篇-炼气期-STL常用函数与数据结构(上篇)

前言

  (双手合十,周身泛起淡淡的代码灵光)诸位道友且慢划走!今天我们不聊金丹元婴那些唬人的大神通,来点实在的——本座夜观天相,发现菜鸟修仙者十有八九不是被红黑二叉树压断灵根,就是在动态规划的心魔劫里走火入魔。但你们可知?只要炼化这枚名为STL的上古储物戒,就能让键盘自动结出算法法印,从此在力扣秘境横着走!(突然压低声音)上个月本座亲眼见证,某个连冒泡排序都要掐诀半柱香的萌新,靠着STL三件套竟在Codeforces战场连破十八铜人阵...(突然闪现到镜头前,手中vector绽放金光)欲知后事如何?且看今日《从炼气到筑基:STLの千层套路》!

算法篇-炼气期-STL常用函数与数据结构(上篇)_第1张图片

本文适合人群:算法比赛萌新,学习C++的兄弟们

STL 作为一个封装良好,性能合格的 C++ 标准库,在算法竞赛中运用极其常见。灵活且正确使用 STL 可以节省非常多解题时间,这一点不仅是由于可以直接调用,还是因为它封装良好,可以让代码的可读性变高,解题思路更清晰,调试过程 往往 更顺利。

排序函数

诸位道友,当你面对十来个乱序的修士要按修为排队时,总不会真的左手拎着for循环炼丹瓶,右手捏着if语句指挥旗,亲自当教导主任吧?看好了!这sort函数可是修真界包邮的自动整队神器—

基础操作:
只需掏出 sort(咸鱼堆.begin(), 咸鱼堆.end(), 比较函数); ,再给这个“比较函数”注入灵魂:(默认是从小到大,这个咸鱼堆可以是结构体或者数组等

实际上在一般的算法题中大部分都涉及到结构体的复杂排序,而不是简单的数组排序

下面是如何实现按结构体中某个元素排序的代码

​
struct 咸鱼修士 {  
    int 修为境界;  // 从996到007不等  
    string 道号;   // 比如"码奴真人"  
    double 摸鱼指数; // 越高越容易在coding时刷淘宝  
};  
//排序函数写在结构体外面(重载的写法-写在结构体内部)
bool 卷王选拔赛(const 咸鱼修士& 甲, const 咸鱼修士& 乙) {  
    // 境界高的排前面,境界相同则摸鱼少的优先  
    if(甲.修为境界 != 乙.修为境界)  
        return 甲.修为境界 > 乙.修为境界;  
    else  
        return 甲.摸鱼指数 < 乙.摸鱼指数;  
}  

​

懒人渡劫:
要是连比较函数都懒得写?直接给结构体穿上运算符重载的仙履:

struct 咸鱼修士 {  
    ...  
    bool operator<(const 咸鱼修士& 他修) const {  
        // 自动按修为升序,摸鱼指数降序排列  
        return tie(修为境界, -摸鱼指数) < tie(他修.修为境界, -他修.摸鱼指数);  
    }  
};  
// 使用时只需 sort(咸鱼堆.begin(), 咸鱼堆.end());  

注意点:结构体排序自定义(注意传递参数的时候用上引用--提高排序的速度

向量vector

#include --引入头文件

构造

vector<类型> arr(长度, [初值])

连续的顺序的储存结构(和数组一样的类别),但是有长度可变的特性。

vector arr;         // 构造int数组
vector arr(100);    // 构造初始长100的int数组
vector arr(100, 1); // 构造初始长100的int数组,初值为1

vector> mat(100, vector ());       // 构造初始100行,不指定列数的二维数组
vector> mat(100, vector (666, -1)) // 构造初始100行,初始666列的二维数组,初值为-1

vector常见操作函数

【尾接 & 尾删】
当你的vector像贪吃蛇修炼成精:

vector 乾坤袋;  // 修真界祖传储物袋

乾坤袋.push_back(114514);  // 往屁股塞灵石,胖+1(均摊下来约等于白嫖)
乾坤袋.push_back(1919810); // 

乾坤袋.pop_back(); // 拉肚子!瘦-1(掉出来的是不是米田共要看元素类型)

【中括号耍流氓】
比御剑飞行还快的随机访问:

vector 仙丹名录 = {"筑基丹", "结金丹", "渡劫丸"};
cout << 仙丹名录[0];  // 输出"筑基丹" —— 注意修真界数组从零开始计数!
仙丹名录[2] = "后悔药"; // 把第三个丹药偷梁换柱(道友:这很修真)

【尺寸把脉术】

if(乾坤袋.size() > 10086) 
    cout << "储物袋要撑爆啦!";  // size()如同照妖镜现原形

while(!仙丹名录.empty()) {  // empty()检查是否被吃干抹净
    cout << "嗑药中..." << endl;
    仙丹名录.pop_back();     // 嗑药姿势请勿模仿
}

【乾坤大挪移】

vector 灵石堆(3, 3.14); // 初始化3块圆周率灵石
灵石堆.resize(5, 6.66);    // 强行扩容到5块,新增的塞入恶魔数字
// 现在灵石堆:[3.14,3.14,3.14,6.66,6.66]

灵石堆.resize(2);          // 渡劫失败被打回原形
// 只剩前两块:[3.14,3.14]

【清空警告】

vector 黑历史 = {'社','死','场','景'};
黑历史.clear();  // 一键消除记忆(注意:并不会释放内存!)
cout << 黑历史.size(); // 输出0,但修真界都知道你只是假装遗忘

普通数组 vs vector:

  • 普通数组:像凡铁打造的储物袋,容量固定还容易划破手

  • vector:自动扩容的须弥芥子空间,自带防爆符(除非遇到卡时间的变态题)

队列queue

#include

通过二次封装双端队列 (deque) 容器,实现先进先出的队列数据结构。

常见用法

作用 用法 示例
构造 queue<类型> que queue que;
进队 .push(元素) que.push(1);
出队 .pop() que.pop();
取队首 .front() int a = que.front();
取队尾 .back() int a = que.back();

 注意事项

不可访问内部元素!--即不能用下标queue[i]直接访问元素

优先队列priority_queue

#include

提供常数时间的最大元素或最小元素查找,对数时间的插入与提取,底层原理是二叉堆。(二叉堆为结丹期内容-敬请期待)

priority_queue<类型, 容器, 比较器> pque

  • 类型:要储存的数据类型
  • 容器:储存数据的底层容器,默认为 vector<类型>,竞赛中保持默认即可
  • 比较器:比较大小使用的比较器,默认为 less<类型>,可自定义

常见用法(重点)

【修真界天梯排行榜——priority_queueの奥义】

核心设定:
这货就是个不讲武德的比武场——永远只能看见榜首大佬,其他修士全被压在五行山下!--最值永远自动排在堆顶(大根堆,小根堆)

基础操作の骚包三连

1. 进堆渡劫(.push())

priority_queue 天梯榜;  // 默认是大佬在上(最大堆)  

天梯榜.push(996);    // 996福报修士入场  
天梯榜.push(007);    // 间谍修士试图上位  
天梯榜.push(114514); // 神秘数字君直接霸榜  
// 当前榜首:114514(自动把最大的顶到最前面)  

2. 榜首飞升(.pop())

天梯榜.pop();  // 榜首渡劫成功,原地飞升  
// 新晋榜首:996(此时007还在山下骂街)  

警告: 空队列使用pop()会引发天劫——段错误雷劈!

3. 偷窥榜首(.top())

if(!天梯榜.empty())  
    cout << "当前卷王:" << 天梯榜.top(); // 输出996  
else  
    cout << "天梯空荡荡,道友在摆烂";  
魔鬼细节の防走火入魔手册

1. 榜单潜规则

  • 只能看榜首(其他道友在渡劫期不可观测):-并且不能用中括号[1]访问-->.top()

  • 全员禁止改命(修真界禁止开挂):

// 作死操作:  
天梯榜.top() = 251; // 试图篡改榜首修为  
// 编译器怒斥:error: top()返回的是const引用!  
  • 正确改命姿势:
int 临时修为 = 天梯榜.top();  
天梯榜.pop();  
天梯榜.push(临时修为 + 10086); // 飞升后重修归来  
自定义比武规则(比较函数の骚操作)

想让弱鸡排前面? 给priority_queue灌反向毒奶

// 小顶堆の秘法(修为低的反而排前面)  
priority_queue, greater> 咸鱼榜;  
咸鱼榜.push(999);  
咸鱼榜.push(1);  
cout << 咸鱼榜.top(); // 输出1 —— 真正的摆烂之王!  

结构体比武大会(接上回咸鱼修士):

struct 咸鱼修士 { /* 同前文定义 */ };  

// 自定义比较器:按摸鱼指数从高到低排列  
struct 摸鱼之王比较器 {  
    bool operator()(咸鱼修士 甲, 咸鱼修士 乙) {  
        return 甲.摸鱼指数 < 乙.摸鱼指数; // 摸鱼越多越牛逼  
    }  
};  

priority_queue<咸鱼修士, vector<咸鱼修士>, 摸鱼之王比较器> 摸鱼榜;  
优先队列适用场景

终极奥义:
当你在LeetCode秘境遇到以下场景,请祭出priority_queue本命法宝:

  1. 要实时找最大/最小(比如合并K个有序数组)

  2. 数据流中持续维护Top K(比如监控修真界打赏榜)

  3. 贪心算法需要(比如宗门派发灵石时优先满足哭得最大声的)

练习题

由于堆在比赛中十分常见,而其他的STL是作为基础,很少有单独考察的。所以作者在这放一道关于堆排序的问题作为练手

Kth Largest Element in an Array - LeetCode

栈stack

#include

通过二次封装双端队列 (deque) 容器,实现先进后出的栈数据结构。

构造

stack<类型> stk

常见用法

【修真界后悔药专卖店——stackの奥义】

核心设定:
这玩意儿就是个只能"后进先出"的炼丹炉——最后塞进去的丹药,总是最先炸炉!

基础操作の痛悟三连

1. 嗑药入栈(.push())

stack 后悔药柜;   // 专门存放"早知道就该..."  

后悔药柜.push("不该用冒泡排序");  
后悔药柜.push("不该在周五晚上提交代码");  
后悔药柜.push("不该和产品经理吵架");  
// 当前炉顶丹药:"不该和产品经理吵架"  

2. 炸炉反悔(.pop())

后悔药柜.pop();  // 吞下最新一颗后悔药  
// 现在炉顶变成:"不该在周五晚上提交代码"  

3. 偷看炉顶(.top())

if(!后悔药柜.empty())  
    cout << "最后悔的事:" << 后悔药柜.top();   
else  
    cout << "无事可悔,道友已成圣";  
禁忌の骚操作

试图偷窥丹炉内部?(走火入魔预警)

// 作死循环法(实际会打印乱码)  
for(int i=0; i<后悔药柜.size(); i++){  
    cout << 后悔药柜[i] << endl; // 报错!stack没有[]操作符  
}  

// 作死迭代法(引发编译器天劫)  
for(auto& 悔意 : 后悔药柜){  
    cout << 悔意; // 报错!stack不支持迭代器  
}  
适用场景の渡劫指南:
  1. 需要"吃后悔药"的场景(函数调用栈、撤销操作)

  2. 对称结构检测(括号匹配、回文校验)

  3. 深度优先搜索(DFS时用来记录路径)

下篇预告

各位道友,经过一番修仙之旅,我们终于走完了STL的基础修炼之路。

下期预告:修仙新境界,探索mapsetstring

第一站:map——藏宝图的秘密
想象你是一位寻宝者,map就是你的藏宝图!它能让你以闪电般的速度找到所需的“宝藏”,堪称修仙界的“索引大师”。下篇我们将深入探讨它的奥秘!

第二站:set——炼丹炉的玄妙
set就像一台神奇的炼丹炉,不仅能去除重复的“杂质”,还能自动排序,炼出纯净的“丹药”。无论是普通丹药还是珍贵灵丹,它都能助你一臂之力!

第三站:string——修仙者的法器
string是程序员手中的法器!它可以帮你轻松处理文字、字符,甚至施展魔法般的操作(比如拼接、查找、替换)。无论是撰写经文还是炼制符咒,它都能让你得心应手!


互动时刻:快来加入我们的修仙之旅!

如果你觉得这篇总结还不错,请记得点个赞、留个言!如果你还没有关注我的博客,也欢迎关注一下,获取更多修仙干货!下篇我们将继续为你揭开STL的神秘面纱,敬请期待!

你可能感兴趣的:(算法修炼篇,算法,c++,数据结构,stl)