栈和队列篇总结
20 有效括号
题目:
给定一个只包括
'('
,')'
,'{'
,'}'
,'['
,']'
的字符串s
,判断字符串是否有效。有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
1
2 输入:s = "()"
输出:true示例 2:
1
2 输入:s = "()[]{}"
输出:true示例 3:
1
2 输入:s = "(]"
输出:false提示:
1 <= s.length <= 104
s
仅由括号'()[]{}'
组成
思路
因为题目中给出了s中的括号由(){}[]
三者组成。遍历字符串, 如果遇到(
就将)
添加进入栈, 如果遇到[
就将]
添加进入栈, {
也是相同的方式。
为什么用这种方式呢, 因为题目中说了
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
所以如果最终出现的括号顺序没错, 那么肯定是先({[
然后再其他的。
之后在遍历到)]}
的时候, 我们就可以直接进行判断, 如果stack.peek() == s[i]
那么就弹出栈 .
最后判断栈是否为空, 如果为空说明我们所有的判断都是对的, 刚好全部匹配上。 反之,如果最后栈不为空或者匹配的时候就出现栈为空的现象, 那么就说明不能匹配成功,直接返回false即可
实现
1 | class Solution { |
150 逆波兰表达式
题目:
给你一个字符串数组
tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。- 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
示例 1:
1
2
3 输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9示例 2:
1
2
3 输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6示例 3:
1
2
3
4
5
6
7
8
9
10 输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22提示:
1 <= tokens.length <= 104
tokens[i]
是一个算符("+"
、"-"
、"*"
或"/"
),或是在范围[-200, 200]
内的一个整数逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
- 平常使用的算式则是一种中缀表达式,如
( 1 + 2 ) * ( 3 + 4 )
。- 该算式的逆波兰表达式写法为
( ( 1 2 + ) ( 3 4 + ) * )
。逆波兰表达式主要有以下两个优点:
- 去掉括号后表达式无歧义,上式即便写成
1 2 + 3 4 + *
也可以依据次序计算出正确结果。- 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
思路
就是一种简单的使用栈的例子。
遍历整个数组, 如果遇到数字 ,就将数字压入栈, 如果遇到符号, 就从栈顶弹出两个数字,进行运算, 然后将运算的结果再压入栈中。 知道遍历完整个数组为止 。
实现
1 | class Solution { |
239 滑动窗口
题目:
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释:
滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1 输出:[1]
提示:
- 1 <= nums.length <= 105
- -104 <= nums[i] <= 104
- 1 <= k <= nums.length
思路:
本题首先相到的是暴力解法, 通过外层遍历获取每个窗口的位置 ,然后再进行内层循环遍历得到每个窗口中的最大值,思路是可行的, 但是时间复杂度是O(n2) 力扣上是过不去的。
接下来想到的是通过使用队列的方式, 将每个窗口都进行入队列操作, 然后得到的内容再进行, 与此同时再对入队列的每个数进行取最大值操作,然后将得到的最大值写入数组中 。通过这样可以将时间复杂度缩小至O(nlogn)但是再力扣中还是过不了。。。
最终通过别人的题解得到了思路是 使用单调队列。
每次添加进队列的数 都是单调递减的, 如果当前需要添加的数 > 队列中的数 ,那么就需要将队列清空。 必须要保证队列中的数是单调递减的 。 这样一旦滑动窗口, 每个窗口的最大值就算当前队列的头部的。 因为我们在入队列的时候就进行了筛选, 必须保证队列是单调递减的。
与此同时,队列中的数是每个窗口内的数进行入队列, 出队列操作,所以我们操作数的范围因该是[i, i + k] 但是存储的是每个数在数组中的索引 ,同时记录的数组范围是 [0 ,i - k + 1 ] 所以选择的数 i - k + 1 必须大于 才能被加入res数组中。
实现
1 | class Solution { |
347 前k个高频元素
题目:
给你一个整数数组
nums
和一个整数k
,请你返回其中出现频率前k
高的元素。你可以按 任意顺序 返回答案。示例 1:
1
2 输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]示例 2:
1
2 输入: nums = [1], k = 1
输出: [1]提示:
1 <= nums.length <= 105
k
的取值范围是[1, 数组中不相同的元素的个数]
- 题目数据保证答案唯一,换句话说,数组中前
k
个高频元素的集合是唯一的进阶:你所设计算法的时间复杂度 必须 优于
O(n log n)
,其中n
是数组大小。
思路
通过Java中的map 实现对每个字符 以及字符出现的次数进行累加。 然后利用stream流的形式再对统计的次数进行排序 (key: 出现的数 value:每个数出现的次数统计
)
最后通过迭代器遍历map, 将前k个依次添加到数组中返回。
实现
1 | class Solution { |