LeetCode四月刷题总结-按类型
这个是四月每日一题的总结,题解基本是官方题解,我按照自己的想法分了一下类方便查阅。主要使用python3解题,如果使用其他语言或者对其他解法感兴趣的可以去官方看哈~prac代表题号。
矩阵相关
采用位置坐标解题
动态规划+搜索
prac542-01矩阵
题目描述:
给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。两个相邻元素间的距离为 1 。
示例 1: 输入:
0 0 0 0 1 0 0 0 0 输出:
0 0 0 0 1 0 0 0 0
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/01-matrix
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# -*- encoding: utf-8 -*-
"""
@File : prac542.py
@Time : 2020/4/15 8:27 上午
@Author : zhengjiani
@Email : 936089353@qq.com
@Software: PyCharm
01矩阵
广度优先搜索可以找到从起点到其他所有点的最短距离
"""
import collections
from typing import List
class Solution:
"""广度优先搜索
时间复杂度O(rc)
"""
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,j-1),(i+1,j),(i,j+1)]:
if 0<=ni<m and 0<=nj<n and (ni,nj) not in seen:
dist[ni][nj] = dist[i][j] + 1
q.append((ni,nj))
seen.add((ni,nj))
return dist
class Solution1:
"""动态规划
需要遍历四次矩阵,时间复杂度O(4rc)=O(rc)
"""
def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
"""
对于矩阵中的任意一个1和一个0,从1到达0距离最短的方法:
- 只有水平向左移动和竖直向上移动
- 只有水平向左移动和竖直向下移动
- 只有水平向右移动和竖直向上移动
- 只有水平向右移动和竖直向下移动
:param matrix:
:return:
"""
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):
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 Solution2:
"""动态规划的常数优化
需要遍历两次矩阵O(2rc)=O(rc)
"""
def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
"""
- 只有 水平向左移动 和 竖直向上移动
- 只有 水平向右移动 和 竖直向下移动
去掉一些重复计算的地方
:param matrix:
:return:
"""
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
if __name__ == '__main__':
# 0 0 0
# 0 1 0
# 0 0 0
matrix = [[0,0,0],
[0,1,0],
[0,0,0]]
s = Solution()
print(s.updateMatrix(matrix))
面试题13
题目描述:
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?示例 1:
输入:m = 2, n = 3, k = 1 输出:3 示例 2:
输入:m = 3, n = 1, k = 0 输出:1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# -*- encoding: utf-8 -*-
"""
@File : 面试题13.py
@Time : 2020/4/8 12:02 下午
@Author : zhengjiani
@Email : 936089353@qq.com
@Software: PyCharm
"""
class Solution:
"""广度优先搜索"""
def movingCount(self, m, n, k):
from queue import Queue
q = Queue()
q.put((0,0))
s = set()
while not q.empty():
x,y = q.get()
if (x,y) not in s and 0<=x<m and 0<=y<n and self.digitsum(x)+self.digitsum(y)<=k:
s.add((x,y))
# 向右、向下搜索
for nx,ny in [((x+1),y),(x,(y+1))]:
q.put((nx,ny))
return len(s)
def digitsum(self,n):
ans = 0
while n:
ans += n%10
n //= 10
return ans
if __name__ == '__main__':
m = 2
n = 3
k = 1
s = Solution()
print(s.monvingCount(m,n,k))
prac200-岛屿数量
题目描述
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入: 11110 11010 11000 00000 输出: 1
示例 2:输入: 11000 11000 00100 00011 输出: 3 解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/number-of-islands
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# -*- encoding: utf-8 -*-
"""
@File : prac200.py
@Time : 2020/4/20 8:28 上午
@Author : zhengjiani
@Email : 936089353@qq.com
@Software: PyCharm
"""
from typing import List
class Solution:
"""
深度优先搜索,时间复杂度O(M*N),M,N为矩阵的行、列数
"""
def dfs(self, grid, r, c):
grid[r][c] = 0
nr, nc = len(grid), len(grid[0])
for x, y in [(r - 1, c), (r + 1, c), (r, c - 1), (r, c + 1)]:
if 0 <= x < nr and 0 <= y < nc and grid[x][y] == "1":
self.dfs(grid, x, y)
def numIslands(self, grid: List[List[str]]) -> int:
nr = len(grid)
if nr == 0:
return 0
nc = len(grid[0])
num_islands = 0
for r in range(nr):
for c in range(nc):
if grid[r][c] == "1":
num_islands += 1
self.dfs(grid, r, c)
return num_islands
if __name__ == '__main__':
grid1 = [["1","1","1","1","0"],["1","1","0","1","0"],["1","1","0","0","0"],["0","0","0","0","0"]]
s = Solution()
print(s.numIslands(grid1))
prac289-生命游戏
# -*- encoding: utf-8 -*-
"""
@File : prac289.py
@Time : 2020/4/2 9:37 上午
@Author : zhengjiani
@Email : 936089353@qq.com
@Software: PyCharm
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态:1 即为活细胞(live),或 0 即为死细胞(dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
根据当前状态,写一个函数来计算面板上所有细胞的下一个(一次更新后的)状态。下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/game-of-life
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
"""
class Solution:
def gameOfLife(self, board):
"""
不返回任何值,仅就地修改细胞板子
"""
# 八个相邻位置
neighbors = [(1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1),(0,1),(1,1)]
rows = len(board)
cols = len(board[0])
# 复制原数组,作为规则引用
copy_board = [[board[row][col] for col in range(cols)] for row in range(rows)]
#双层循环遍历数组
for row in range(rows):
for col in range(cols):
# 对于每一个细胞统计其八个相邻位置活细胞数量
live_neighbors = 0
for neighbor in neighbors:
r = (row + neighbor[0])
c = (col + neighbor[1])
# 查看相邻的细胞是否为活细胞
if (r<rows and r>=0) and (c<cols and c>=0) and (copy_board[r][c]==1):
live_neighbors += 1
# 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
# 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
if copy_board[row][col] == 1 and (live_neighbors < 2 or live_neighbors > 3):
board[row][col] = 0
# 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
if copy_board[row][col] == 0 and live_neighbors == 3:
board[row][col] = 1
class Solution1:
"""定义复合状态,原地改变数组
例子:如果细胞之前的状态是 0,但是在更新之后变成了 1,我们就可以给它定义一个复合状态 2
"""
def gameOfLife(self, board):
# 八个相邻位置
neighbors = [(1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0), (-1, 1), (0, 1), (1, 1)]
rows = len(board)
cols = len(board[0])
# 双层循环遍历数组
for row in range(rows):
for col in range(cols):
# 对于每一个细胞统计其八个相邻位置活细胞数量
live_neighbors = 0
for neighbor in neighbors:
r = (row + neighbor[0])
c = (col + neighbor[1])
# 查看相邻的细胞是否为活细胞
if (r < rows and r >= 0) and (c < cols and c >= 0) and (abs(board[r][c]) == 1):
live_neighbors += 1
# 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
# 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
if board[row][col] == 1 and (live_neighbors < 2 or live_neighbors > 3):
# -1代表这个细胞过去是火的现在死了
board[row][col] = -1
# 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
if board[row][col] == 0 and live_neighbors == 3:
# 2代表这个细胞过去是死的现在活了
board[row][col] = 2
# 遍历board得到一次更新后的状态
for row in range(rows):
for col in range(cols):
if board[row][col] > 0:
board[row][col] = 1
else:
board[row][col] = 0
if __name__ == '__main__':
board = [
[0, 1, 0],
[0, 0, 1],
[1, 1, 1],
[0, 0, 0]
]
s = Solution1()
s.gameOfLife(board)
print(board)
容器盛水问题
prac42-接雨水
双指针求法
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1] 输出: 6
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/trapping-rain-water
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# -*- encoding: utf-8 -*-
"""
@File : prac42.py
@Time : 2020/4/4 8:49 上午
@Author : zhengjiani
@Email : 936089353@qq.com
@Software: PyCharm
单调栈,单调队列
"""
class Solution:
"""按行求解,提交后超出时间限制"""
def trap(self, height):
sum = 0
max_h = max(height)
for i in range(1,max_h+1):
# 标记是否开始更新
is_start = False
temp = 0
for j in range(len(height)):
# 如果当前的高度小于 i,并且两边有高度大于等于 i 的,说明这个地方一定有水,水就可以加 1
if is_start and height[j] < i:
temp += 1
# 遇到高度大于等于 i 的,就把 temp 加到最终的答案 ans 里,并且 temp 置零,然后继续循环。
if height[j] >= i:
sum = sum + temp
temp = 0
is_start = True
return sum
def get_max(self,height):
"""
找到最大高度
:param height:
:return:
"""
max_height = 0
for i in range(len(height)):
if height[i]>max_height:
max_height = height[i]
return max_height
class Solution1:
"""双指针求法"""
def trap(self, height):
n = len(height)
if n == 0:
return 0
left = 0
right = n -1
# max_left [i] 代表第 i 列左边最高的墙的高度
max_left = height[left]
# max_right[i] 代表第 i 列右边最高的墙的高度
max_right = height[right]
ans = 0
while(left < right):
if max_left <= max_right:
ans += max_left-height[left]
left += 1
max_left = max(height[left],max_left)
if max_left > max_right:
ans += max_right-height[right]
right -= 1
max_right = max(height[right],max_right)
return ans
class Solution3:
"""栈的求法,类似括号匹配"""
def trap(self, height):
"""
将每堵墙作为栈元素入栈
:param height:
:return:
"""
sum = 0
stack = []
current = 0
# 如果当前高度<栈顶高度,则入栈
while(current<len(height)):
# 如果栈不空且当前高度大于栈顶高度就一直循环
while(len(stack) != 0 and height[current] > height[stack[-1]]):
h = height[stack[-1]]
# 如果当前高度>栈顶高度,就出栈
stack.pop()
if len(stack) == 0:
break
# 计算两堵墙之间的距离
distance = current - stack[-1] - 1
min_h = min(height[stack[-1]],height[current])
sum = sum + distance * (min_h - h)
stack.append(current)
current += 1
return sum
if __name__ == '__main__':
height = [0,1,0,2,1,0,1,3,2,1,2,1]
s = Solution3()
print(s.trap(height))
prac11-盛最多水的容器
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
输入:[1,8,6,2,5,4,8,3,7] 输出:49
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/container-with-most-water
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
# -*- encoding: utf-8 -*-
"""
@File : prac11.py
@Time : 2020/4/18 9:22 上午
@Author : zhengjiani
@Email : 936089353@qq.com
@Software: PyCharm
容纳的水量 = 两个指针指向的数字中较小值*指针之间的距离
"""
from typing import List
class Solution:
"""双指针解法"""
def maxArea(self, height: List[int]) -> int:
l, r = 0,len(height)-1
ans = 0
while l < r:
area = min(height[l],height[r])*(r-l)
ans = max(ans,area)
if height[l] <= height[r]:
l += 1
else:
r -= 1
return ans
if __name__ == '__main__':
lis = [1,8,6,2,5,4,8,3,7]
s = Solution()
print(s.maxArea(lis))