目录
一、用栈实现队列
1.1 题目要求
1.2 解决过程
个人实现
法一:双栈 (列表模拟)。弹出 pop 的操作先将主栈 self.queue 中所有元素换入辅助栈 temp 中,再取出最后一个元素 (对应队首元素),然后再将辅助栈 temp 的元素换回主栈 self.queue 中,时间复杂度 O(n)。其余操作均为 O(1) 复杂度。
2020/09/12 - 87.99% (36ms) - 用 collections.deque() 替换 list 也一样。
class MyQueue:
def __init__(self):
"""
Initialize your data structure here.
"""
self.queue = [] # 用 list 模拟栈实现队列
def push(self, x: int) -> None:
"""
Push element x to the back of queue.
"""
# 复杂度 O(1)
self.queue.append(x)
def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
# 复杂度 O(n)
if self.queue:
temp = [] # 辅助交换栈
# 换出
while self.queue:
temp.append(self.queue.pop())
# 弹出栈顶元素
elem = temp.pop()
# 换入
while temp:
self.queue.append(temp.pop())
return elem
def peek(self) -> int:
"""
Get the front element.
"""
# 复杂度 O(1)
return self.queue[0] if self.queue else None
def empty(self) -> bool:
"""
Returns whether the queue is empty.
"""
# 复杂度 O(1)
return False if self.queue else True
# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()
法二:双栈 (列表模拟)。不同于法一使用双栈处理出队 pop 操作,此处使用双栈处理入队 push 操作,使之复杂度为 O(n),其余操作复杂度为 O(1)。
2020/09/12 - 87.99% (36ms) - 用 collections.deque() 替换 list 也一样。
class MyQueue:
def __init__(self):
self.queue = [] # 用 list 模拟栈实现队列
def push(self, x: int) -> None:
# 复杂度 O(n)
temp = []
while self.queue:
temp.append(self.queue.pop())
temp.append(x)
while temp:
self.queue.append(temp.pop())
def pop(self) -> int:
# 复杂度 O(1)
return self.queue.pop() if self.queue else None
def peek(self) -> int:
# 复杂度 O(1)
return self.queue[-1] if self.queue else None
def empty(self) -> bool:
# 复杂度 O(1)
return False if self.queue else True
官方实现与说明
// Java implementation
private int front;
public void push(int x) {
if (s1.empty())
front = x;
while (!s1.isEmpty())
s2.push(s1.pop());
s2.push(x);
while (!s2.isEmpty())
s1.push(s2.pop());
}
// Java implementation
// Removes the element from the front of queue.
public void pop() {
s1.pop();
if (!s1.empty())
front = s1.peek();
}
// Java implementation
// Return whether the queue is empty.
public boolean empty() {
return s1.isEmpty();
}
// Java implementation
// Get the front element.
public int peek() {
return front;
}
// Java implementation
private Stack<Integer> s1 = new Stack<>();
private Stack<Integer> s2 = new Stack<>();
// Push element x to the back of queue.
public void push(int x) {
if (s1.empty())
front = x;
s1.push(x);
}
// Java implementation
// Removes the element from in front of queue.
public void pop() {
if (s2.isEmpty()) {
while (!s1.isEmpty())
s2.push(s1.pop());
}
s2.pop();
}
// Java implementation
// Return whether the queue is empty.
public boolean empty() {
return s1.isEmpty() && s2.isEmpty();
}
// Java implementation
// Get the front element.
public int peek() {
if (!s2.isEmpty()) {
return s2.peek();
}
return front;
}
参考文献
https://leetcode-cn.com/leetbook/read/queue-stack/gvtxe/
二、用队列实现栈
2.1 题目要求
2.2 解决过程
个人实现
法一:双队列 (双端队列模拟)。操作主要集中于出栈,等价地,把操作主要集中于入栈也可以。出栈复杂度 O(n),其余操作复杂度为 O(1)。
2020/09/12 - 95.86% (32ms)
class MyStack:
def __init__(self):
"""
Initialize your data structure here.
"""
self.stack1 = collections.deque() # 主栈
self.stack2 = collections.deque() # 副栈
def push(self, x: int) -> None:
"""
Push element x onto stack.
"""
if self.stack2:
self.stack2.append(x)
else:
self.stack1.append(x) # 默认入主栈
def pop(self) -> int:
"""
Removes the element on top of the stack and returns that element.
"""
if self.stack1:
while len(self.stack1) > 1:
self.stack2.append(self.stack1.popleft())
return self.stack1.popleft()
elif self.stack2:
while len(self.stack2) > 1:
self.stack1.append(self.stack2.popleft())
return self.stack2.popleft()
else:
return None
def top(self) -> int:
"""
Get the top element.
"""
if self.stack1:
return self.stack1[-1]
elif self.stack2:
return self.stack2[-1]
else:
return None
def empty(self) -> bool:
"""
Returns whether the stack is empty.
"""
return not (self.stack1 or self.stack2)
# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()
法一改:双队列。通过交换主栈 self.stack 和辅助栈 self.temp 操作简化条件判断语句的书写。出栈复杂度 O(n),其余操作复杂度 O(1)。
2020/09/12 - 87.41% (36ms)
class MyStack:
def __init__(self):
self.stack = collections.deque() # 主栈
self.temp = collections.deque() # 辅助栈
def push(self, x: int) -> None:
self.stack.append(x)
def pop(self) -> int:
if self.stack:
while len(self.stack) > 1:
self.temp.append(self.stack.popleft())
self.stack, self.temp = self.temp, self.stack # 交换操作
return self.temp.popleft()
def top(self) -> int:
if self.stack:
return self.stack[-1]
def empty(self) -> bool:
return not self.stack
官方实现与说明
# Python implementation
class MyStack:
def __init__(self):
self.stack = collections.deque() # 主栈
def push(self, x: int) -> None:
self.stack.append(x)
size = len(self.stack)
for _ in range(size-1): # 重要
self.stack.append(self.stack.popleft())
def pop(self) -> int:
if self.stack:
return self.stack.popleft()
def top(self) -> int:
if self.stack:
return self.stack[0]
def empty(self) -> bool:
return not self.stack
2020/09/12 - 69.08% (40ms)
参考文献
https://leetcode-cn.com/leetbook/read/queue-stack/gw7fg/
三、字符串解码
3.1 题目要求
3.2 解决过程
测试用例
要点:单位数/多位数、单字母/多字母、线性编码/嵌套编码
"3[a]2[bc]"
"3[a2[c]]"
"2[abc]3[cd]ef"
"abc3[cd]xyz"
"100[leetcode]"
个人实现
法二:栈 (双端队列模拟) + 迭代。遍历字符串 s,在遇到 "]" 前,字符 c 不断入栈 Q。一旦遇到 "]",则表明需要进行一次解码。使用双端队列 temp 保存临时的连续字符,并在遇到 "[" 时结束 temp 的增加,然后继续弹出次数并保存于双端队列 num 中,最后转换为解码表示字符串 string,入栈 Q。此处采用先无差别入栈,事后再出栈处理的顺序。当然,也可以事前处理完,再入栈。空间复杂度 O(n),时间复杂度 O(n)。
2020/09/12 - 95.43% (32ms)
class Solution:
def decodeString(self, s: str) -> str:
Q = collections.deque() # 辅助双端队列
for c in s: # 遍历已编码字符串
if c != ']': # 没遇到 "]" 前无差别不断入栈 (事后出栈处理)
Q.append(c)
else:
alp = collections.deque() # 新建临时连续字母队列
while Q: # 在循环内进行一次解码
end = Q.pop()
if end.isalpha(): # 若为字母
alp.appendleft(end) # 字母入队
else: # 否则为 "[" , 其前必为数字, 表示出现次数
num = collections.deque() # 新建临时连续数字队列
while Q and Q[-1].isdigit(): # 在循环内获取次数
num.appendleft(Q.pop())
times = int("".join(num)) # 解码次数
string = "".join(alp * times) # 解码字符串 (不用转换为 list 也可以用 * 和 join())
Q.append(string) # 入栈
break # 本次解码完成, 退出循环
return "".join(Q) # 最终解码字符串
官方实现与说明
// C++ implementation
class Solution {
public:
string getDigits(string &s, size_t &ptr) {
string ret = "";
while (isdigit(s[ptr])) {
ret.push_back(s[ptr++]);
}
return ret;
}
string getString(vector <string> &v) {
string ret;
for (const auto &s: v) {
ret += s;
}
return ret;
}
string decodeString(string s) {
vector <string> stk;
size_t ptr = 0;
while (ptr < s.size()) {
char cur = s[ptr];
if (isdigit(cur)) {
// 获取一个数字并进栈
string digits = getDigits(s, ptr);
stk.push_back(digits);
} else if (isalpha(cur) || cur == '[') {
// 获取一个字母并进栈
stk.push_back(string(1, s[ptr++]));
} else {
++ptr;
vector <string> sub;
while (stk.back() != "[") {
sub.push_back(stk.back());
stk.pop_back();
}
reverse(sub.begin(), sub.end());
// 左括号出栈
stk.pop_back();
// 此时栈顶为当前 sub 对应的字符串应该出现的次数
int repTime = stoi(stk.back());
stk.pop_back();
string t, o = getString(sub);
// 构造字符串
while (repTime--) t += o;
// 将构造好的字符串入栈
stk.push_back(t);
}
}
return getString(stk);
}
};
class Solution {
public:
string src;
size_t ptr;
int getDigits() {
int ret = 0;
while (ptr < src.size() && isdigit(src[ptr])) {
ret = ret * 10 + src[ptr++] - '0';
}
return ret;
}
string getString() {
if (ptr == src.size() || src[ptr] == ']') {
// String -> EPS
return "";
}
char cur = src[ptr]; int repTime = 1;
string ret;
if (isdigit(cur)) {
// String -> Digits [ String ] String
// 解析 Digits
repTime = getDigits();
// 过滤左括号
++ptr;
// 解析 String
string str = getString();
// 过滤右括号
++ptr;
// 构造字符串
while (repTime--) ret += str;
} else if (isalpha(cur)) {
// String -> Char String
// 解析 Char
ret = string(1, src[ptr++]);
}
return ret + getString();
}
string decodeString(string s) {
src = s;
ptr = 0;
return getString();
}
};
其他实现与说明
class Solution:
def decodeString(self, s: str) -> str:
stack, res, multi = [], "", 0
for c in s:
if c == '[':
stack.append([multi, res])
res, multi = "", 0
elif c == ']':
cur_multi, last_res = stack.pop()
res = last_res + cur_multi * res
elif c.isdigit():
multi = multi * 10 + int(c)
else:
res += c
return res
2020/09/12 - 66.86% (40ms)
class Solution:
def decodeString(self, s: str) -> str:
def dfs(s, i):
res, multi = "", 0
while i < len(s):
if s[i].isdigit():
multi = multi * 10 + int(s[i])
elif s[i] == '[':
i, tmp = dfs(s, i + 1)
res += multi * tmp
multi = 0
elif s[i] == ']':
return i, res
else:
res += s[i]
i += 1
return res
return dfs(s, 0)
2020/09/12 - 66.86% (40ms)
参考文献
https://leetcode-cn.com/leetbook/read/queue-stack/gdwjv/
https://leetcode-cn.com/problems/decode-string/solution/zi-fu-chuan-jie-ma-by-leetcode-solution/
四、图像渲染
4.1 题目要求
4.2 解决过程
个人实现
测试用例
[[1,1,1],[1,1,0],[1,0,1]]
1
1
2
[[0,0,0],[0,0,0]]
0
0
2
[[0,0,0],[0,1,1]]
1
1
1
法一:DFS 递归实现。空间复杂度 O(m×n),时间复杂度 O(m×n)。
2020/08/16 - 90.80% (88ms)
class Solution:
def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -> List[List[int]]:
def dfs(row, col, oldColor, newColor, height, width):
# 当前像素重渲染
image[row][col] = newColor
# 向上渲染
up = row - 1
if up >= 0 and image[up][col] == oldColor:
dfs(up, col, oldColor, newColor, height, width)
# 向下渲染
down = row + 1
if down < height and image[down][col] == oldColor:
dfs(down, col, oldColor, newColor, height, width)
# 向左渲染
left = col - 1
if left >= 0 and image[row][left] == oldColor:
dfs(row, left, oldColor, newColor, height, width)
# 向右渲染
right = col + 1
if right < width and image[row][right] == oldColor:
dfs(row, right, oldColor, newColor, height, width)
# 渲染完毕
return
oldColor = image[sr][sc] # 旧像素颜色
# 需要渲染才操作, 以避免新旧颜色一致而陷入死循环, 这很关键
if newColor != oldColor:
height = len(image) # 图画高度 (行数 row 上限)
width = len(image[0]) # 图画宽度 (列数 col 上限)
dfs(sr, sc, oldColor, newColor, height, width) # DFS
return image
法二:BFS 递归实现。空间复杂度 O(m×n),时间复杂度 O(m×n)。
2020/08/16 - 61.82% (96ms) - 代价更大
class Solution:
def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -> List[List[int]]:
if image[sr][sc] == newColor: # 需要渲染才操作
return image
oldColor = image[sr][sc] # 旧像素颜色
height = len(image) # 图画高度 (行数 row 上限)
width = len(image[0]) # 图画宽度 (列数 col 上限)
Q = collections.deque() # 待渲染像素队列
Q.append((sr, sc)) # 起点像素入队
while Q:
# 当前像素坐标出队并渲染
cur_sr, cur_sc = Q.popleft()
image[cur_sr][cur_sc] = newColor
# 向上渲染
up = cur_sr - 1
if up >= 0 and image[up][cur_sc] == oldColor:
Q.append((up, cur_sc))
# 向下渲染
down = cur_sr + 1
if down < height and image[down][cur_sc] == oldColor:
Q.append((down, cur_sc))
# 向左渲染
left = cur_sc - 1
if left >= 0 and image[cur_sr][left] == oldColor:
Q.append((cur_sr, left))
# 向右渲染
right = cur_sc + 1
if right < width and image[cur_sr][right] == oldColor:
Q.append((cur_sr, right))
return image
官方实现与说明
class Solution:
def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -> List[List[int]]:
currColor = image[sr][sc]
if currColor == newColor:
return image
n, m = len(image), len(image[0])
que = collections.deque([(sr, sc)])
image[sr][sc] = newColor
while que:
x, y = que.popleft()
for mx, my in [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]:
if 0 <= mx < n and 0 <= my < m and image[mx][my] == currColor:
que.append((mx, my))
image[mx][my] = newColor
return image
class Solution:
def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -> List[List[int]]:
n, m = len(image), len(image[0])
currColor = image[sr][sc]
def dfs(x: int, y: int):
if image[x][y] == currColor:
image[x][y] = newColor
for mx, my in [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]:
if 0 <= mx < n and 0 <= my < m and image[mx][my] == currColor:
dfs(mx, my)
if currColor != newColor:
dfs(sr, sc)
return image
参考文献
https://leetcode-cn.com/leetbook/read/queue-stack/g02cj/
https://leetcode-cn.com/problems/flood-fill/
https://leetcode-cn.com/problems/flood-fill/solution/tu-xiang-xuan-ran-by-leetcode-solution/
五、01 矩阵
5.1 题目要求
5.2 解决过程
测试用例
[[0,0,0],[0,1,0],[0,0,0]]
[[0,0,0],[0,1,0],[1,1,1]]
官方实现与说明
class Solution:
def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
# 矩阵高度和宽度
m, n = len(matrix), len(matrix[0])
# 结果距离矩阵 - 零值初始化
dist = [[0]*n for _ in range(m)]
# 零元素坐标列表
zeroes_pos = [(i, j) for i in range(m) for j in range(n) if matrix[i][j] == 0]
# 将所有的零元素坐标添加进初始队列
q = collections.deque(zeroes_pos)
# 已发现位置集合 - 以所有零元素位置初始化
seen = set(zeroes_pos)
# 广度优先搜索
while q:
i, j = q.popleft() # 当前位置坐标
for ni, nj in [(i - 1, j), (i + 1, j), (i, j - 1), (i, j + 1)]:
if (0 <= ni < m) and (0 <= nj < n) and ((ni, nj) not in seen):
dist[ni][nj] = dist[i][j] + 1 # 与当前位置距离 +1
q.append((ni, nj)) # 坐标入队
seen.add((ni, nj)) # 坐标加入已发现位置集合
return dist
2020/09/13 - 75.53% (772ms)
class Solution:
def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
m, n = len(matrix), len(matrix[0])
# 初始化动态规划的数组,所有的距离值都设置为一个很大的数
dist = [[10**9] * n for _ in range(m)]
# 如果 (i, j) 的元素为 0,那么距离为 0
for i in range(m):
for j in range(n):
if matrix[i][j] == 0:
dist[i][j] = 0
# 只有 水平向左移动 和 竖直向上移动,注意动态规划的计算顺序
for i in range(m):
for j in range(n):
if i - 1 >= 0:
dist[i][j] = min(dist[i][j], dist[i - 1][j] + 1)
if j - 1 >= 0:
dist[i][j] = min(dist[i][j], dist[i][j - 1] + 1)
# 只有 水平向左移动 和 竖直向下移动,注意动态规划的计算顺序
for i in range(m - 1, -1, -1):
for j in range(n):
if i + 1 < m:
dist[i][j] = min(dist[i][j], dist[i + 1][j] + 1)
if j - 1 >= 0:
dist[i][j] = min(dist[i][j], dist[i][j - 1] + 1)
# 只有 水平向右移动 和 竖直向上移动,注意动态规划的计算顺序
for i in range(m):
for j in range(n - 1, -1, -1):
if i - 1 >= 0:
dist[i][j] = min(dist[i][j], dist[i - 1][j] + 1)
if j + 1 < n:
dist[i][j] = min(dist[i][j], dist[i][j + 1] + 1)
# 只有 水平向右移动 和 竖直向下移动,注意动态规划的计算顺序
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
if i + 1 < m:
dist[i][j] = min(dist[i][j], dist[i + 1][j] + 1)
if j + 1 < n:
dist[i][j] = min(dist[i][j], dist[i][j + 1] + 1)
return dist
class Solution:
def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
m, n = len(matrix), len(matrix[0])
# 初始化动态规划的数组,所有的距离值都设置为一个很大的数
dist = [[10**9] * n for _ in range(m)]
# 如果 (i, j) 的元素为 0,那么距离为 0
for i in range(m):
for j in range(n):
if matrix[i][j] == 0:
dist[i][j] = 0
# 只有 水平向左移动 和 竖直向上移动,注意动态规划的计算顺序
for i in range(m):
for j in range(n):
if i - 1 >= 0:
dist[i][j] = min(dist[i][j], dist[i - 1][j] + 1)
if j - 1 >= 0:
dist[i][j] = min(dist[i][j], dist[i][j - 1] + 1)
# 只有 水平向右移动 和 竖直向下移动,注意动态规划的计算顺序
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
if i + 1 < m:
dist[i][j] = min(dist[i][j], dist[i + 1][j] + 1)
if j + 1 < n:
dist[i][j] = min(dist[i][j], dist[i][j + 1] + 1)
return dist
参考文献
https://leetcode-cn.com/leetbook/read/queue-stack/g7pyt/
https://leetcode-cn.com/problems/01-matrix/solution/01ju-zhen-by-leetcode-solution/
六、钥匙和房间
6.1 题目要求
6.2 解决过程
个人实现
法一:双端队列 + 哈希集合 + BFS。空间复杂度 O(n),时间复杂度 O(n)。
2020/09/18 - 98.82% (72ms)
class Solution:
def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
keys = {0} # 已拥有钥匙集合, 一开始就拥有 0 号房间的钥匙
Q = collections.deque() # 辅助队列 - 待搜索房间队列
Q.append(rooms[0]) # 一开始在 0 号房间
while Q:
room = Q.popleft() # 当前房间
for key in room: # 当前房间的各个钥匙
if key in keys: # 若已拥有过该房间的钥匙, 则不再收集和处理
continue
keys.add(key) # 否则, 新增钥匙
Q.append(rooms[key]) # 并将对应房间加入待搜索房间队列
return len(keys) == len(rooms)
官方实现与说明
class Solution:
def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
def dfs(x):
visited.add(x)
nonlocal num_key
num_key += 1
for key in rooms[x]:
if key not in visited: # 未探索过才进行探索
dfs(key)
num_key = 0 # 已拥有钥匙数
visited = set() # 已探索房间集合
dfs(0) # 从 0 号房间开始
return num_key == len(rooms)
2020/09/18 - 73.28% (84ms)
class Solution:
def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
num_key = 0
visited = {0}
queue = collections.deque([0])
while queue:
x = queue.popleft()
num_key += 1
for i in rooms[x]:
if i not in visited:
visited.add(i)
queue.append(i)
return num_key == len(rooms)
2020/09/18 - 88.00% (80ms)
参考文献
https://leetcode-cn.com/leetbook/read/queue-stack/gle1r/
https://leetcode-cn.com/problems/keys-and-rooms/solution/yao-chi-he-fang-jian-by-leetcode-solution/