【代码随想录Day 4 - 栈和队列③ 6~9 】

Carl 语录

提醒

个人总结

一句话

1. C++中 有哪些将字符串转换为其他类型的函数?

以下是一些常用的转换函数:

std::stoi():将字符串转换为int类型。
std::stol():将字符串转换为long类型。
std::stoul():将字符串转换为unsigned long类型。
std::stoll():将字符串转换为long long类型。
std::stoull():将字符串转换为unsigned long long类型。
std::stof():将字符串转换为float类型。
std::stod():将字符串转换为double类型。
std::stold():将字符串转换为long double类型。

这些函数都定义在头文件中,并且都接受一个const std::string&类型的参数,以及可选的转换基数(对于整数类型)和指向停止位置的指针。

使用示例:

#include <iostream>  
#include <string>  
  
int main() {  
    std::string str;  
    int num_int;  
    long num_long;  
    double num_double;  
  
    str = "123";  
    num_int = std::stoi(str); // 将字符串转换为int类型  
    std::cout << "int: " << num_int << std::endl;  
  
    str = "4567890123";  
    num_long = std::stol(str); // 将字符串转换为long类型  
    std::cout << "long: " << num_long << std::endl;  
  
    str = "3.14159";  
    num_double = std::stod(str); // 将字符串转换为double类型  
    std::cout << "double: " << num_double << std::endl;  
  
    return 0;  
}

请注意,如果字符串的内容无法转换为目标类型,或者转换后的值超出了目标类型的表示范围,这些函数将抛出std::invalid_argument或std::out_of_range异常。因此,在实际使用时,建议将这些函数调用放在try-catch块中,以处理潜在的异常。

除了标准库提供的这些函数外,还可以使用C风格的函数,如atoi(), atol(), atoll(), atof()等,但这些函数在错误处理方面不如C++风格的函数强大,因此通常不推荐使用。另外,也可以自己编写解析函数来实现更复杂的字符串到类型的转换逻辑。

2. C语言中将字符串转换为相应的数值类型的函数?

C风格的函数,如atoi(), atol(), atoll(), 和 atof(),用于将字符串转换为相应的数值类型。这些函数在C语言标准库中定义,并且在C++中仍然可用,尽管在C++中更推荐使用标准库提供的转换函数(如std::stoi(), std::stol(), std::stoll(), std::stof()等),因为它们提供了更好的错误处理机制。

  • atoi():将字符串转换为int类型
    函数原型:int atoi(const char *str);
    str是要转换的字符串。
    如果转换失败,则返回0。
  • atol():将字符串转换为long类型。
    函数原型:long atol(const char *str);
    str是要转换的字符串。
    如果转换失败,则返回0L。
  • atoll():将字符串转换为long long类型。
    函数原型:long long atoll(const char *str);
    注意:不是所有的C库都提供这个函数,但在一些平台上是可用的,或者可以通过定义宏或编写自己的函数来实现。
    str是要转换的字符串。
    如果转换失败,则通常返回0LL。
  • atof():将字符串转换为double类型。
    函数原型:double atof(const char *str);
    str是要转换的字符串。
    如果转换失败或字符串不包含可识别的数字,则返回0.0。

这些函数在解析字符串时会跳过前导的空白字符,并尝试将尽可能多的字符转换为相应的数值类型。如果字符串以非数字字符开始,或者表示的数超出了目标类型的范围,那么转换就会失败,并返回特定的默认值(通常是0或0.0)。

重要的是要注意,这些函数不提供错误处理的机制,比如报告转换过程中遇到的特定问题。因此,在使用这些函数时,你需要特别小心,并确保输入字符串的格式是你所期望的。如果可能的话,使用C++标准库提供的转换函数是更好的选择,因为它们提供了更强大的错误处理功能。

3. bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) { }

这段代码定义了一个函数对象(也称为仿函数或functor),通常用于作为自定义的比较函数。在C++中,函数对象是一种可以像函数一样被调用的对象,通常通过重载operator()来实现。在这个例子中,该函数对象用于比较两个pair<int, int>对象。

详细解释如下:

函数对象声明:

bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs)

这定义了一个名为operator()的成员函数,该函数接受两个类型为pair<int, int>的常量引用参数:lhs(代表“left-hand side”,即左操作数)和rhs(代表“right-hand side”,即右操作数)。函数返回一个布尔值(bool),表示比较的结果。
2. 比较逻辑:

return lhs.second > rhs.second;

这里,函数比较了两个pair对象的second成员(即每个pair的第二个元素)。如果lhs的second值大于rhs的second值,函数返回true;否则返回false。

这种比较函数通常用于自定义排序算法,例如std::sort。例如,如果你有一个vector<pair<int, int>>,并希望根据每个pair的第二个元素进行降序排序,你可以使用这个函数对象作为std::sort的第三个参数:

std::vector<std::pair<int, int>> vec = {{1, 3}, {2, 1}, {3, 4}};  
std::sort(vec.begin(), vec.end(), YourFunctor());

其中YourFunctor是包含上述operator()定义的类或结构体的类型。排序后,vec将按每个pair的第二个元素降序排列。

需要注意的是,为了完整地使用这个函数对象,你需要将其放在一个类或结构体中,例如:

4. pair<int,int>

pair<int, int> 是C++标准库中的一个模板类,它用于存储两个值的有序对。在这个特定的例子中,pair的两个值都是int类型。因此,你可以将pair<int, int>理解为一个包含两个整数的有序对数据类型。

每个pair对象都有两个成员变量:first和second,分别用于访问其存储的第一个和第二个值。你可以通过这两个成员变量来获取或设置pair对象中的值。

以下是一些使用pair<int, int>的示例:

定义和初始化:

pair<int, int> p1(1, 2); // 创建一个pair对象p1,其中first为1,second为2
通过成员变量访问和修改值:


p1.first = 3;    // 修改p1的第一个值为3  
p1.second = 4;   // 修改p1的第二个值为4
使用make_pair函数创建pair对象:


pair<int, int> p2 = make_pair(5, 6); // 使用make_pair函数创建一个pair对象p2

pair在C++中非常有用,特别是在需要同时处理两个相关值的情况下。例如,在STL(标准模板库)的map容器中,键(key)和值(value)就是以pair的形式存储的。

5. priority_queue< >

这段代码是C++中的一部分,用于定义一个priority_queue。这里的priority_queue是一个STL(标准模板库)中的容器适配器,它提供了一个优先队列的接口,即队列中元素的优先级最高(或最低)的元素总是位于队列的顶部。

具体解释如下:

  1. priority_queue<…> pri_que;: 定义一个名为pri_que的优先队列。
  2. pair<int, int>: 优先队列中的元素是pair<int, int>类型,即每个元素都是一个包含两个整数的有序对。
  3. vector<pair<int, int>>: 优先队列的底层容器是vector,它存储了pair<int, int>类型的元素。
  4. mycomparison: 这是一个自定义的比较函数或函数对象,用于确定队列中元素的优先级。默认情况下,priority_queue是一个最大堆,即优先级最高的元素(值最大的元素)位于队列的顶部。如果你希望改变这个行为(例如,使其成为一个最小堆),你需要提供一个自定义的比较函数或函数对象。

在这个例子中,mycomparison应该是一个已经定义好的类或函数对象,它重载了operator()以提供自定义的比较逻辑。这个比较逻辑将用于确定priority_queue中元素的优先级。

总结起来,priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;这行代码定义了一个名为pri_que的优先队列,它使用pair<int, int>作为元素类型,使用vector作为底层容器,并使用mycomparison来确定元素的优先级。


150. 逆波兰表达式求值

一刷无思路
150. 逆波兰表达式求值

实现目标:

将数字压入栈中,

思路:

  • 遍历容器,如果碰到运算符 就取两个栈顶元素进行运算,并将结果压回栈中。
  • 否则(就是数字),将string转换类型后压入栈中。

实现过程:

  1. 坑点① 测试过程中有 大数,必须要用 long long 类型
  2. 坑点② string类型必须用 双引号 “”
  3. 坑点③ 数字的顺序,因为是先入后出,所以后出的元素实则在运算符之前,与二叉树后序遍历相同
  4. 坑点④ string类型转为long long,可用 stoll() 函数(string to long long)

代码:

class Solution {
public:
    int evalRPN(vector<string>& tokens) {

        //坑点① 测试过程中有 大数,必须要用 long long 类型
        stack<long long> digits;
        for(string s : tokens){

            // 坑点② string类型必须用 双引号 ""
            if( s == "+" || s == "-" || s == "*" || s == "/" )
            {
                long long num1 = digits.top();
                digits.pop();
                long long num2 = digits.top();
                digits.pop();

                //坑点③ 数字的顺序,因为是先入后出,所以后出的元素实则在运算符之前,与二叉树后序遍历相同
                if(s == "+") digits.push(num2 + num1);
                if(s == "-") digits.push(num2 - num1);
                if(s == "*") digits.push(num2 * num1);
                if(s == "/") digits.push(num2 / num1);                
            }
            
            else{
                //坑点④ string类型转为long long,可用 stoll() 函数(string to long long)
                digits.push(stoll(s));
            }
        }
        return digits.top();
    }
};

239. 滑动窗口最大值

一刷无思路
239. 滑动窗口最大值

实现目标:

① 求出当前窗口中最大值max;
② 将每个窗口的max保存。

思路:

  1. 如果新加入的元素 >= 上一轮 max,直接成为新一轮max;
  2. 否则记 max = 当前窗口第一个元素,遍历窗口,更新max;
  3. 重复1、2,直至窗口到达 [ n-k-1 ] ~ [ n-1 ] ;

实现过程:

代码:(暴力解法,超时未通过)

超时,未通过
时间复杂度:O(n x k),已通过 37/51,但是超时了。

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        
        if(k == 1) return nums;

        vector<int> result;
        //坑点① 不可以 int max = nums[0];
        int max =INT_MAX;

        //leetcode里没有max()函数
        if(nums.size() == 2 && k == 2) result.push_back(nums[0]>nums[1]?nums[0]:nums[1]);
        else{
            for(int i = 0; i < nums.size() + 1 - k ; i++){

                //坑点② max也要及时更新
                //新加入窗口的元素,若比上一轮最大数还大,则成为新的max
                if(nums[i+k-1] >= max)  max = nums[i+k-1] ;

                //否则,在新的窗口中逐个比较,得到该轮最大值max
                else{
                    max = nums[i];
                    for(int j = i+1; j < i+k ; j++){
                        max = max >= nums[j] ? max : nums[j];
                    }
                }
                result.push_back(max);              
            }
        }
        return result;
    }
};

Carl 方法 – 单调队列

代码随想录的分析与题解

思路:单调队列
  • 特点:队列中的元素单调递减——需要返回窗口中最大值时,就弹出队首元素。
  • 实现:
  • push() : 放入元素时,若元素值 > 队首元素,则将队内所有元素清除,将新元素放在队首;若元素值 <= 队首元素,则依次比较,按递减顺序放置。
  • pop() :清除队首元素;
  • front() :返回队首元素。
代码
class Solution {

private:
    class MyQueue{
        public:
            deque<int> que;

            //滑动窗口右移,对刚脱离窗口的旧元素进行判断,若其原为最大元素,则将其删去。
            //如果它不是原最大元素的呢,岂不是删除不了? 不会的,如果它不是最大的,那么在push()阶段就被后来的更大者给淘汰了
            void pop( int value){
                // 
                if(!que.empty() && value == que.front()) que.pop_front();
            }

            int front(){
                //坑点:非void函数必须有返回值,if(可能性事件){return} 是非法行为
                //if(1) 可以
                return que.front();
            }

            // 滑动窗口右移,对新元素进行判断,选择其位置
            void push(int value){
                // 繁杂方法:从前往后删除还得设计插入新元素的方式
                // if (value > que.front()) while(!que.empty()) {que.pop();}

                // 优雅方法:只要新元素不是最小,就可以从后往前比较,删除更小者
                while(!que.empty() && value > que.back()) que.pop_back();
                que.push_back(value);
            }
    };

public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MyQueue que;
        vector<int> result;
        for(int i = 0; i < k; i++){
            que.push(nums[i]);
        }
        //result记录前k个中最大的元素
        result.push_back(que.front());  

        for(int i = k; i < nums.size(); i++){
            que.pop(nums[i-k]);  //滑动窗口移除最前面元素
            que.push(nums[i]);   //滑动窗口添加最后面元素
            result.push_back(que.front()); //记录当前窗口最大值
        } 
        return result;
    } 
};

347.前 K 个高频元素

一刷无思路
347.前 K 个高频元素

实现目标:

① 统计各元素的频次;
② 对频次排序;
③ 找出频次最大的k个元素

思路:

  • ① unoreded_map<int,int>
  • ②③ 优先级队列——使用小根堆,不断排除最小的元素,最后留下的就是最大的几个数
    • priority_queue(待排序元素,容纳排序元素的容器,排序方法)
  • ④ vector result , 循环k次 pri_que.top().first ,pri_que.pop() ,将k个元素装入result中

实现过程:

  1. 坑点 :C++的class默认情况下访问修饰符为private,如果这里不设置public,则会导致外界无法访问

代码:

纯抄Carl的代码
参考代码


题型总结:栈与队列

单调队列
优先级队列

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值