算法面试相关专题:
北大硕士LeetCode算法专题课--链表相关问题_
北大硕士LeetCode算法专题课-查找相关问题_
北大硕士LeetCode算法专题课-字符串相关问题_
北大硕士LeetCode算法专题课-数组相关问题_ _
北大硕士LeetCode算法专题课-基础算法之排序_
北大硕士LeetCode算法专题课-基础算法查找_
北大硕士LeetCode算法专题课---算法复杂度介绍_
什么是栈
栈 (stack),是一种以后进先出的方式存取数据的数据结构(LIFO Last-In/First-Out) 可以通过编辑器中的撤销(undo)功能来加深对栈的理解
假设我们在IDE中编辑一份Python代码,首先创建了一个函数,IDE的撤销功能就会将创建函数的操作添加到 Undo Stack中
添加了一个函数后, 我们又删除了几个单词, IDE会向 Undo Stack 中Push 对应的操作
结下来,我们又调整了一下注释的缩进,IDE的Undo Stack 也会记录相应的操作。向栈中添加元素的操作成为 Push。 最后加入的元素会放在栈顶
此时我们发现,之前编辑的函数有问题, 需要撤销之前的所有操作,此时最先撤销的时缩进调整的操作。 从栈中移除元素的操作称为 pop。
接下来撤销的是 删除文字的操作
最后撤销的是添加函数的操作。 整个过程是按照LIFO顺序执行的
栈的实现
可以使用Python 的 list 来实现一个栈, 我们可以使用 list 中的 append 来替换栈中的Push。使用list中 的 pop方法来实现 从栈中按照LIFO的顺序移除数据的操作
也可以使用Python 的 Collection模块中的 deque 来实现一个栈,调用的方法与Python的list完全一样 deque 是双端队列
还可以通过 queue. LifoQueue 来实现栈。 它提供了put() 和 get() 方法,一个用于push数据, 另一个用于pop
from queue import LifoQueue
if name == ' main ': myStack = LifoQueue() myStack.put('a')
myStack.put('b')
myStack.put('c') print(myStack.queue) print(myStack.get()) print(myStack.queue) print(myStack.get()) print(myStack.queue)
queue. LifoQueue 与 list和deque 的区别. LifoQueue线程安全, list 线程不安全,deque 的append 和pop是线程安全的, 其余不安全
有效的括号(Leetcode 20)
如给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效 有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合
def isValid(s):
if len(s) % 2 == 1:
return False
pairs = {
")": "(",
"]": "[",
"}": "{",}
stack = list()
for ch in s:
if ch in pairs:
if not stack or stack[-1] != pairs[ch]:
return False stack.pop()
else:
stack.append(ch)
return not stack
复杂度分析 :时间复杂度:O(n) 空间复杂度:O(n)
最小栈(Leetcode 155)
如设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。 实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。
如设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
什么是队列
队列 (queue),是一种以先进先出的方式存取数据的数据结构(FIFO First-In/First-Out)
Queue 的特点类似一个管子, 我们从管子的一端放入元素, 从另一端取出元素。按照FIFO的方式排列元素,向队列中添加元素 的操作称为 Enqueue, 从队列中移除元素的操作称为 Dequeue
Python colletion模块中的 deque (double-ended queue) 可以借助deque (双端队列) 来实现队列
from collections import deque
if name == ' main ': customers = deque()
# 开始排队 customers.append("Jane") customers.append("John") customers.append("Linda") print(customers)
# 进场离队 print(customers.popleft()) print(customers) print(customers.popleft()) print(customers) print(customers.popleft())
数据流中的移动平均值 (Leetcode 346)
给定一个整数数据流和一个窗口大小,根据该滑动窗口的大小,计算其所有整数的移动平均值。 实现 MovingAverage 类:
MovingAverage(int size) 用窗口大小 size 初始化对象。
double next(int val) 计算并返回数据流中最后 size 个值的移动平均值。
思路 一: 利用数组/列表
class MovingAverage:
def init (self, size: int): self.size = size self.queue = []
def next(self, val: int) -> float: size, queue = self.size, self.queue queue.append(val)
# calculate the sum of the moving window window_sum = sum(queue[-size:])
return window_sum / min(len(queue), size)
思路 二: 利用队列
from collections import deque
class MovingAverage:
def init (self, size: int): self.size = size self.queue = deque() self.window_sum = 0
self.count = 0 # 用来记录加进来多少元素
def next(self, val: int) -> float: self.count += 1
# 向队列中加入数据
self.queue.append(val)
# 如果加入数据条目数>窗口大小, 队列移除队头数据
tail = self.queue.popleft() if self.count > self.size else 0 self.window_sum = self.window_sum - tail + val
return self.window_sum / min(self.size, self.count)
用队列实现栈 (Leetcode 225)
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty) 实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
滑动窗口最大值 (Leetcode 239) 困难
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的
思路 一: 优先队列
优先队列:出队顺序与入队顺序无关,和优先级有关 普通队列:先进先出,后进后出
一般用最大堆/最小堆 来实现优先队列
Python中的heapq模块 实现了优先队列,可以借助该模块解决当前问题。
滑动窗口最大值 (Leetcode 239) 困难
思路 二: 单调递减队列
实际上没有必要维护一个长度为K的队列,我们的目标是获取最大值, 那么那些不可能成为窗口最大值的元素, 可以去掉
为了方便判断队首元素与滑动窗口的位置关系,队列中保存的是对应元素的下标。
思路 二: 单调递减队列
思路 二: 单调递减队列 具体思路如:初始时单调队列为空,随着对数组的遍历过程中,每次插入中的