提示0:抱歉来晚了,后面坚决准时更新~
提示1:本篇共7道力扣题目供大家食用,时间自行把控~
算法刷题系列
文章目录
作者有话说
1、本篇是算法刷题系列文章的第 5
篇,写此系列的目的是为了让自己对题目的理解更加深刻。
2、本系列博客主要参考了卡哥的 代码随想录博客 以及 卡哥本人B站讲解的视频 代码随想录B站视频 ,强烈推荐给大家,因为本人学习中 Python为主
,因此博客主要由 Python
代码呈现给大家,需要其他语言的版本,卡哥博客链接自取。
一、栈与队列
1.1 栈
先进后出,这里有A
、B
、C
、D
四个数据,依次压入栈中,则栈中的元素是 D
、C
、B
、A
,然后依次出栈,出栈顺序的是:D --> C --> B --> A 。
1.2 队列
先进先出,这里还以上述的例子为例,有A
、B
、C
、D
四个数据,依次压入队中,则队中的元素是 A
、B
、C
、D
,然后依次出队,出队顺序的是:A --> B --> C --> D 。
二、经典题目
2.1 LeetCode232. 用栈实现队列
- 原题地址: 232. 用栈实现队列
- 题目描述: 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
- 解题思路: 一个栈代表入栈,一个栈代表出栈,从而实现栈模拟队列。
- 代码如下:
class MyQueue:
def __init__(self):
self.stack_in = []
self.stack_out = []
def push(self, x: int) -> None:
self.stack_in.append(x)
def pop(self) -> int:
if self.empty():
return None
if self.stack_out:
return self.stack_out.pop()
else:
for i in range(len(self.stack_in)):
self.stack_out.append(self.stack_in.pop())
return self.stack_out.pop()
def peek(self) -> int:
result = self.pop()
self.stack_out.append(result)
return result
def empty(self) -> bool:
return not (self.stack_in or self.stack_out)
2.2 LeetCode225. 用队列实现栈
- 原题地址: 225. 用队列实现栈
- 题目描述: 请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
- 解题思路: 栈的核心是:“先进后出”,而队列是:“先进先出”。此题的核心在于如何模拟出栈,这里给出的方法是:假如队列中有
size
个元素,我们让size-1
个元素先出队再入队,这样就可以实现用队列模拟栈的效果。 - 代码如下:
class MyStack:
def __init__(self):
self.queue = []
def push(self, x: int) -> None:
self.queue.append(x)
def pop(self) -> int:
if self.empty():
return None
size = len(self.queue)
for i in range(size-1):
self.queue.append(self.queue.pop())
return self.queue.pop()
def top(self) -> int:
if self.empty():
return None
return self.queue[-1]
def empty(self) -> bool:
return not self.queue
2.3 LeetCode20. 有效的括号
- 原题地址: 20. 有效的括号
- 题目描述: 给定一个只包括
'(',')','{','}','[',']'
的字符串s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合;
- 左括号必须以正确的顺序闭合;
- 每个右括号都有一个对应的相同类型的左括号。
- 解题思路: 遍历输入字符串,如果是
(
、[
、{
等左括号,便将对应的右括号压入栈中,当遍历的是右括号时,与栈中的元素进行消消乐,若相同栈中元素弹出进入下一轮,若不同直接返回False
。当循环遍历完成后,要考虑栈是否为空的情况,为空返回True
,否则返回False
。 - 代码如下:
法1代码:
class Solution:
def isValid(self, s: str) -> bool:
stack = []
for item in s:
if item == '(':
stack.append(')')
elif item == '[':
stack.append(']')
elif item == '{':
stack.append('}')
elif not stack or stack[-1] != item:
return False
else:
stack.pop()
return True if not stack else False
法2代码:
def isValid(self, s: str) -> bool:
if len(s) % 2 != 0:
return False
stack = list()
hashmap = {
")": "(",
"]": "[",
"}": "{",
}
for ch in s:
if ch in hashmap:
if not stack or stack[-1] != hashmap[ch]:
return False
stack.pop()
else:
stack.append(ch)
return not stack
2.4 LeetCode1047. 删除字符串中的所有相邻重复项
- 原题地址: 1047. 删除字符串中的所有相邻重复项
- 题目描述: 给出由小写字母组成的字符串
S
,重复项删除操作 会选择两个相邻且相同的字母,并删除它们。在S
上反复执行重复项删除操作,直到无法继续删除。在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。 - 解题思路: 借助栈这个数据结构来攻克这个题,思想有点类似于
有效的括号
那道题,我们先遍历元素,入栈,然后遍历下一个,若与栈顶元素相同则消消乐
,否则压入栈中,继续遍历。重复以上操作,直到遍历完全部的元素。
- 代码如下:
class Solution:
def removeDuplicates(self, s: str) -> str:
stack = []
for ch in s:
if stack and stack[-1] == ch:
stack.pop()
else:
stack.append(ch)
return "".join(stack) # 字符串拼接
2.5 LeetCode150. 逆波兰表达式求值
- 原题地址: 150. 逆波兰表达式求值
- 题目描述: 根据 逆波兰表示法,求表达式的值。
有效的算符包括+、-、*、/
。每个运算对象可以是整数,也可以是另一个逆波兰表达式。 - 逆波兰记法: 逆波兰记法中,操作符置于操作数的后面。例如表达“三加四”时,写作“3 4 +”,而不是“3 + 4”。如果有多个操作符,操作符置于第二个操作数的后面,所以常规中缀记法的“3 - 4 + 5”在逆波兰记法中写作“3 4 - 5 +”:先3减去4,再加上5。使用逆波兰记法的一个好处是不需要使用括号。摘自百度百科。
- 解题思路: 借助栈,遍历
token
,遇到数字压入栈中,遇到运算符弹出两个元素进行计算,然后将计算结果重新压入栈中。重复上述操作,直到遍历结束。【2版代码】 - 代码如下:
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
stack = []
for ch in tokens:
if ch not in {"+", "-", "*", "/"}:
stack.append(ch)
else:
num1, num2 = stack.pop(), stack.pop()
num1, num2 = int(num1), int(num2)
if (ch == "+"): stack.append(num2 + num1)
if (ch == "-"): stack.append(num2 - num1)
if (ch == "*"): stack.append(num2 * num1)
if (ch == "/"): stack.append(num2 / num1)
return int(stack.pop())
from operator import add, sub, mul
class Solution:
op_map = {'+': add, '-': sub, '*': mul, '/': lambda x, y: int(x / y)}
def evalRPN(self, tokens: List[str]) -> int:
stack = []
for token in tokens:
if token not in {'+', '-', '*', '/'}:
stack.append(int(token))
else:
op2 = stack.pop()
op1 = stack.pop()
stack.append(self.op_map[token](op1, op2))
return stack.pop()
2.6 LeetCode239. 滑动窗口最大值
- 原题地址: 239. 滑动窗口最大值
- 题目描述: 给你一个整数数组
nums
,有一个大小为k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k
个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值 。 - 解题思路: 此题借助单调栈的思路去解决,博主也还在理解中,这里给出卡哥的视频讲解,大家去听一听。卡哥视频地址
- 代码如下: 卡哥的python版本,注释很详细,大家一起理解一下。
from collections import deque
class MyQueue: #单调队列(从大到小
def __init__(self):
self.queue = deque()
def pop(self, value):
if self.queue and value == self.queue[0]:
self.queue.popleft()
def push(self, value):
while self.queue and value > self.queue[-1]:
self.queue.pop()
self.queue.append(value)
def front(self):
return self.queue[0]
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
que = MyQueue()
result = []
for i in range(k): #先将前k的元素放进队列
que.push(nums[i])
result.append(que.front()) #result 记录前k的元素的最大值
for i in range(k, len(nums)):
que.pop(nums[i - k]) #滑动窗口移除最前面元素
que.push(nums[i]) #滑动窗口前加入最后面的元素
result.append(que.front()) #记录对应的最大值
return result
2.7 LeetCode347. 前 K 个高频元素
- 原题地址: 347.前 K 个高频元素
- 题目描述: 给你一个整数数组
nums
和一个整数k
,请你返回其中出现频率前k
高的元素。你可以按 任意顺序 返回答案。 - 解题思路: 题目需要做3件事:1、统计元素出现的频率;2、排序;3、找出前K个高频元素。使用字典结构去处理,key代表元素,value统计频率。因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。
- 代码如下:
import heapq
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
#要统计元素出现频率
map_ = {}
for i in range(len(nums)):
map_[nums[i]] = map_.get(nums[i], 0) + 1
#对频率排序
#定义一个小顶堆,大小为k
pri_que = [] #小顶堆
#用固定大小为k的小顶堆,扫描所有频率的数值
for key, freq in map_.items():
heapq.heappush(pri_que, (freq, key))
if len(pri_que) > k:
heapq.heappop(pri_que)
result = [0] * k
for i in range(k-1, -1, -1):
result[i] = heapq.heappop(pri_que)[1]
return result
总结
栈与队列篇
到这里就结束了,最后两题有一定的难度大家花一点时间多看看体会一下。最后,若文章中有表述不当的地方还望大家多多指出,二叉树篇
见吧。