C++刷题笔记(14)——leetcode150、239

题目1:150.逆波兰表达式求值

在这里插入图片描述
逆波兰表达式又叫做后缀表达式,即将运算符写在操作数之后,是一种没有括号、严格遵循从左到右运算的后缀式表达方法。

表达式E的后缀形式
如果E是E1 op E2形式的表达式,这里op是任何二元操作符,则E的后缀式为E1’E2’ op,这里E1’和E2’分别为E1和E2的后缀式。

如:(1 + 2) * (3 + 4)写成后缀形式为1 2 + 3 4 + *

解法:栈

逆波兰表达式适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算(先出栈的是右操作数,后出栈的是左操作数),并将结果压入栈中。

class Solution {
public:
	int evalRPN(vector<string>& tokens) {
		stack<int> st;
		for (int i = 0; i < tokens.size(); i++) {
			if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") {
				int rightnum = st.top();
				st.pop();
				int leftnum = st.top();
				st.pop();
				if (tokens[i] == "+") st.push(leftnum + rightnum);
				if (tokens[i] == "-") st.push(leftnum - rightnum);
				if (tokens[i] == "*") st.push(leftnum * rightnum);
				if (tokens[i] == "/") st.push(leftnum / rightnum);
			}
			else {
				st.push(stoi(tokens[i]));   //stoi:数字字符串转换成int输出
			}
		}
		return st.top();
	}
};

题目2:239.滑动窗口最大值

在这里插入图片描述

解法一:优先队列

C++ STL priority_queue容器适配器详解

优先队列priority_queue遵循"first in,largest out"原则,先进队列的元素并不一定先出队列,而是优先级最大的元素先出队列,默认按照元素从大到小进行排序。

优先级队列可以理解为披着队列外衣的堆,堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。

采用最大堆的数据结构来保存元素,堆顶元素即为当前堆的最大值,并判断当前堆顶元素这是否在窗口中,在则直接返回,不在则删除堆顶元素并调整堆。
在这里插入图片描述
窗口遍历数组
在这里插入图片描述

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> result;
        priority_queue<pair<int, int>> pq;

        for (int i = 0; i < k; i++) {                      //将数组的前k个元素放入优先队列中
            pq.push(make_pair(nums[i], i));                //或pq.emplace(nums[i], i);
        }
        result.push_back(pq.top().first);                  //堆顶就是最大元素

        for (int i = k; i < nums.size(); i++) {            //滑动窗口从索引为k的元素开始遍历,将新进入滑动窗口的元素加堆中
            pq.push(make_pair(nums[i], i));               
            while (pq.top().second <= i - k) {             //当堆顶元素不在滑动窗口中的时候,不断删除堆顶堆元素,直到最大值在滑动窗口里
                pq.pop();
            }
            result.push_back(pq.top().first);
        }
        return result;
    }
};

解法二:单调队列(双端队列)——最优解

单调队列即队列中所有的元素都是单调的,要么单增要么单减;
新增元素时添加到队尾,若队列不符合单调性就将队尾元素出队,直到队列单调或空。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
之后的遍历同理。

注意,队列中保存的并不是数,而是数组中该数的下标

还要考虑当队首元素不在滑动窗口时的情况:
在这里插入图片描述
L、R来表示窗口的左边界和右边界,R可以用下标i来表示,那么左边界L为i-k+1当队首元素的下标小于滑动窗口左边界L时表示队首元素已经不再滑动窗口内

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> q;                                                //双端队列
        for (int i = 0; i < k; i++) {                                //将数组的前k个元素放入单调队列中
            while (!q.empty() && nums[i] >= nums[q.back()]) {        //如果遍历到的元素大于等于单调队列队尾元素,则不满足从大到小
                q.pop_back();                                        //那么就将队尾元素移除
            }
            q.push_back(i);                                          //如果队列为空则跳过while循环并将遍历到的元素添加到队尾
        }
        vector<int> result = { nums[q.front()] };                    //窗口大小为k,将队首的元素放进结果数组里
        for (int i = k; i < nums.size(); i++) {                      //滑动窗口从索引为k的元素开始遍历
            while (!q.empty() && nums[i] >= nums[q.back()]) {
                q.pop_back();
            }
            q.push_back(i);
            while (q.front() <= i - k) {                             //判断队首的元素是否在滑动窗口范围内  等同于q.front() <= i - k + 1
                q.pop_front();
            }
            result.push_back(nums[q.front()]);
        }
        return result;
    }
};

评论区大佬更巧妙的代码:

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int>q; //双端队列
        vector<int>res;
        for (int i = 0; i < nums.size(); i++) {
            while (q.size() && i - k + 1 > q.front())  q.pop_front(); //判断队头是否在滑动窗口范围内
            while (q.size() && nums[i] >= nums[q.back()]) q.pop_back();//维护单调递减队列
            q.push_back(i); //将当前元素插入队尾
            if (i >= k - 1)  res.push_back(nums[q.front()]); //滑动窗口的元素达到了k个,才可以将其加入答案数组中
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用中的代码片段是一个递归函数,名为beautifulArray,它接受一个整数n作为参数,并返回一个vector<int>类型的结果。这个函数用于生成一个长度为n的漂亮数组,漂亮数组满足以下条件:如果将其划分为两个任意长度的子数组,那么两个子数组的元素之和不相等。 函数的实现思路是通过递归,将问题划分为两个子问题,然后合并子问题的解。首先判断n是否等于1,如果等于1,则直接返回一个包含1的数组。否则,将n减1后的一半传入递归函数beautifulArray,得到一个漂亮数组res1。然后将n的一半传入递归函数beautifulArray,得到另一个漂亮数组res2。最后,将res1中的每个元素乘以2并减去1,然后与res2中的每个元素乘以2合并到结果数组res中。 引用中的代码片段是一个函数,名为translateNum,它接受一个整数num作为参数,并返回一个整数。这个函数用于计算将整数num翻译成字符串的不同方法数。 函数的实现思路是将整数num转换为字符串str,然后创建一个长度为str.size()-1的动态数组dp,用于记录不同位置的翻译方法数。然后初始化dp和dp[1]为1,表示前两位数字的翻译方法数。接下来,从第三位数字开始遍历字符串str,如果当前数字与前两位数字可以构成一个在10到25之间的整数,则将dp[i]的值设置为dp[i-1]+dp[i-2],表示当前位置的翻译方法数为前一位和前两位数字的翻译方法数之和。最后,返回dp最后一个元素的值,即为翻译整数num的不同方法数。 引用中的代码片段是一个函数,名为numberOfArithmeticSlices,它接受一个vector<int>类型的数组nums作为参数,并返回一个整数。这个函数用于计算数组nums中等差子序列的个数。 函数的实现思路是通过动态规划,创建一个与nums长度相同的动态数组dp,用于记录以每个位置为结尾的等差子序列的个数。然后遍历数组nums,从第三个元素开始,判断当前元素与前两个元素是否构成等差数列,如果是,则将dp[i]的值设置为dp[i-1]加1,表示以当前位置为结尾的等差子序列个数为前一位的等差子序列个数加1。最后,返回dp数组中所有元素的和,即为等差子序列的个数。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值