LeetCode Cookbook 数组习题(2)
篇接上回,开始!
41. 缺失的第一个正数*
题目链接:41. 缺失的第一个正数
题目大意:给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
n = len(nums)
for i in range(n):
while 1 <=nums[i]<=n and nums[nums[i]-1] != nums[i]:
nums[nums[i]-1],nums[i] = nums[i],nums[nums[i]-1]
print(nums)
for i in range(n):
if nums[i] != i+1:
return i+1
return n+1
782. 变为棋盘
题目链接:782. 变为棋盘
题目大意:一个 n x n 的二维网络 board 仅由 0 和 1 组成 。每次移动,你能任意交换两列或是两行的位置。返回 将这个矩阵变为 “棋盘” 所需的最小移动次数 。如果不存在可行的变换,输出 -1。“棋盘” 是指任意一格的上下左右四个方向的值均与本身不同的矩阵。
- 完成计数器以及字符串列表的创建
- 过滤不能成为棋盘得几种情况
- 利用数组特有的索引奇数偶数特性进行交换次数的判断
class Solution:
def movestochessboard(self, board: List[List[int]]) -> int:
n = len(board)
if n<1: return 0
# rows = ['0110', '0110', '1001', '1001']
rows = [''.join(str(c) for c in r) for r in board]
# Counter({'0110': 2, '1001': 2})
counter = collections.Counter(rows)
# keys = ['0110', '1001'])
keys = list(counter)
# *args = *list ====> 将list直接传递给args,但类型转换为元组
if len(keys) != 2 or abs(counter[keys[0]]-counter[keys[1]]) > 1 \
or abs(keys[0].count('1')-keys[0].count('0'))>1 \
or any(a==b for a,b in zip(*keys)):
return -1
# 分别计算行与列所需的交换次数 利用数列的奇偶特性进行行列不同的划分
rowDiff = sum(board[0][i] != (i%2) for i in range(n))
colDiff = sum(board[i][0] != (i%2) for i in range(n))
# 选择大的交换次数
rowDiff = n-rowDiff if rowDiff%2!=0 or (n%2==0 and (n-rowDiff)<rowDiff) else rowDiff
colDiff = n-colDiff if colDiff%2!=0 or (n%2==0 and (n-colDiff)<colDiff) else colDiff
# 求平均
return (rowDiff+colDiff)//2
42. 接雨水*
题目链接:42. 接雨水
题目大意:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
解题思路: 双头指针,两头一起走 高的一侧不动 低的一侧行走
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
ans = 0
L,R = 0,n-1
Lmax,Rmax = 0,0
while L<R:
Lmax = max(Lmax,height[L])
Rmax = max(Rmax,height[R])
if height[L]<height[R]:
# 低得先走
ans += Lmax - height[L]
L += 1
else:
ans += Rmax - height[R]
R -= 1
return ans
48. 旋转图像*
题目链接:48. 旋转图像
题目大意:给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
解题思路: 矩阵中的许多细节操作:
- 列按照对称轴交换 :找出对称轴,一次交换,注意循环前后顺序;
- 各行元素按照斜对角进行交换:先交换横纵坐标,而后用(n-1)分别减去新的坐标。
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
n = len(matrix)
# 列交换
for j in range(n):
for i in range(n//2):
matrix[j][i],matrix[j][n-1-i] = matrix[j][n-1-i],matrix[j][i]
print(matrix)
# 对角交换
# 1.交换行列 2.n-1减去交换后的行列
for i in range(n-1,0,-1):
for j in range(0,i):
# (n-1)-j and n-1-(n-1-i)
matrix[n-1-i][j],matrix[n-1-j][i] = matrix[n-1-j][i],matrix[n-1-i][j]
53. 最大子数组和
题目链接:53. 最大子数组和
题目大意:给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。
解题思路: 动态规划,需要推一下使用的方程,主要是动态数组的获取,只要动态数组当前索引下是正的就一直往后加if dp[i-1] >= 0: dp[i] = dp[i-1] + nums[i]
,如果是负的就让当前的值进行替换dp[i] = nums[i]
。
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
# 看题解好像要用 动态规划
n = len(nums)
dp = [0]*(n)
dp[0] = nums[0]
for i in range(1,n):
if dp[i-1] >= 0:
dp[i] = dp[i-1] + nums[i]
else:
dp[i] = nums[i]
return max(dp)
剑指 Offer 29. 顺时针打印矩阵 And 54. 螺旋矩阵*
题目链接:剑指 Offer 29. 顺时针打印矩阵和54. 螺旋矩阵
题目大意:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
解题思路: 没什么技巧,搞好小脚标!
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if len(matrix) == 0: return []
m,n = len(matrix),len(matrix[0])
top,bottom,left,right = 0,m-1,0,n-1
ans = []
while len(ans) < m*n:
for j in range(left,right+1):
ans.append(matrix[top][j])
top += 1
if top>bottom: break
for i in range(top,bottom+1):
ans.append(matrix[i][right])
right -= 1
if left>right: break
for j in range(right,left-1,-1):
ans.append(matrix[bottom][j])
bottom -= 1
if top>bottom: break
for i in range(bottom,top-1,-1):
ans.append(matrix[i][left])
left += 1
if left > right:break
return ans
59. 螺旋矩阵 II
题目链接:59. 螺旋矩阵 II
题目大意:给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
解题思路: 思路和上面的剑指 Offer 29. 顺时针打印矩阵和54. 螺旋矩阵非常相近,注意了!python乘方是两个星星啊! n**1
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
top,bottom,left,right = 0,n-1,0,n-1
ans = [[ 0 for _ in range(n)] for _ in range(n)]
numCur,numSum = 1,n**2
while numCur <= numSum:
for j in range(left,right+1):
ans[top][j] = numCur
numCur += 1
top += 1
for i in range(top,bottom+1):
ans[i][right] = numCur
numCur += 1
right -= 1
for j in range(right,left-1,-1):
ans[bottom][j] = numCur
numCur += 1
bottom -= 1
for i in range(bottom,top-1,-1):
ans[i][left] = numCur
numCur += 1
left += 1
return ans
55. 跳跃游戏**
题目链接:55. 跳跃游戏
题目大意:给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标。
解题思路: 非常巧妙的一道题,里面涉及到 贪心和动态规划,其实主要的精华就在这一句话中rightMost = max(rightMost,i+nums[i])
,需要深度理解这里面最右面的位置这句话,太特么神奇了!!!
class Solution:
def canJump(self, nums: List[int]) -> bool:
n = len(nums)
if n == 1: return True
i,rightMost = 0,0
# 果然理解错了 需要有一个最远点的定义 无论如何也需要遍历一边数组
for i in range(n):
if i <= rightMost:
rightMost = max(rightMost,i+nums[i])
if rightMost >= n-1: return True
return False
62. 不同路径*
题目链接:62. 不同路径
题目大意:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。问总共有多少条不同的路径?
解题思路: 模板题 有简单办法 记住吧!
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
# 贪心
'''
return comb(m+n-2,m-1)
'''
# 传统
dp = [[1]*n] + [[1]+[0]*(n-1) for _ in range(m-1)]
# print(f)
for i in range(1,m):
for j in range(1,n):
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[m-1][n-1]
63. 不同路径 II
题目链接:63. 不同路径 II
题目大意:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?网格中的障碍物和空位置分别用 1 和 0 来表示。
解题思路: 与62. 不同路径很像,但稍微复杂一些
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
m,n = len(obstacleGrid),len(obstacleGrid[0])
dp = [[0]*n for _ in range(m)]
for i in range(n):
# 左上角是障碍物 直接结束
if obstacleGrid[0][i] == 1: break
dp[0][i] = 1
for j in range(m):
if obstacleGrid[j][0] == 1:
break
dp[j][0] = 1
for i in range(1,m):
for j in range(1,n):
if obstacleGrid[i][j] == 1: continue
dp[i][j] = dp[i][j-1] + dp[i-1][j]
return dp[m-1][n-1]
64. 最小路径和
题目链接:64. 最小路径和
题目大意:给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。说明:每次只能向下或者向右移动一步。
解题思路:62. 不同路径的扩展变形
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m,n = len(grid),len(grid[0])
for i in range(1,m):
grid[i][0] += grid[i-1][0]
for j in range(1,n):
grid[0][j] += grid[0][j-1]
for i in range(1,m):
for j in range(1,n):
grid[i][j] += min(grid[i-1][j],grid[i][j-1])
return grid[m-1][n-1]
66. 加一
题目链接:66. 加一
题目大意:给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。
解题思路:
- 传统办法恢复原数加一而后拆开,虽然笨,但挺快的!
- GF解法 非常的巧妙啊! 控住 9 啥都好说
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
# 传统办法
ans,num,n = [],0,len(digits)-1
for i in digits:
num += i * 10**(n)
n -= 1
num += 1
while num:
ans.append(num%10)
num //= 10
return ans[::-1]
'''
# 非常棒的办法
n = len(digits)
for i in range(n-1,-1,-1):
if digits[i] != 9:
digits[i] += 1
for j in range(i+1,n):
digits[j] = 0
return digits
# 如果 99
return [1]+[0]*n
'''
74. 搜索二维矩阵 And 剑指 Offer 04. 二维数组中的查找*
题目链接:74. 搜索二维矩阵 And 剑指 Offer 04. 二维数组中的查找
题目大意:编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
解题思路: 下楼梯的方法
class Solution:
def searchmatrix(self, matrix: List[List[int]], target: int) -> bool:
# 定位在右上角
i,j = len(matrix)-1,0
while i >=0 and j<len(matrix[0]):
if target > matrix[i][j]:
j += 1
elif target < matrix[i][j]:
i -= 1
else:
return True
return False
总结
昨天写好的,今天检查了一下再发出去,前天看书的时候知道我们的银河系的直径最大在160亿光年,而光的速度在这巨大的直径面前如此的龟速,这不仅让人想到银河系是否就是一个高度瘫痪的“病人”,它无法像人一样可以实现头部到脚步的信号传递,因而是否银河系是一个即将面临生命终结的“病人”。这属实让我震惊了,我现在发现好多的修仙小说、玄幻漫画或者科幻漫画,或许这些创作者不是故意以人文基调或者以感性思维来宏观解释美丽的却又悲怆的宇宙,而是摸索出或嗅出一丝真相。或许这位“病人”距离自己生命的逝去还有一段时间,但这段时间相对于我的一生长的不可思议,所以就不想那么多了。
另外,前段时间看了阿西莫夫的《最后的问题》的解说,非常地令人感触,一切都将归于沉寂,一切又始于荒芜。在20世纪科幻作品产出丰富的年代,这些作家以前所未有的勇气和魄力进行思想上的开阔与探索,但隐藏在这之下的毫无疑问是一种淡淡的悲伤,就如弗洛伊德的核心观点一样,我们可以抗拒命运,但无法改变命运。我最近可能陷入一个注意力不足的阶段,我需要阅读足够的书籍缓冲度过这个不安的时期,最后,一如既往地,努力,奋斗!