LeetCode Cookbook 数组习题(9) 终篇
数组部分最后一篇了,这篇内容可能比较水,都是一些简单题,也不知道作者是不是 最后安慰一下 我这刷题小白。
1260. 二维网格迁移
题目链接:1260. 二维网格迁移
题目大意:给你一个 m 行 n 列的二维网格 grid 和一个整数 k。你需要将 grid 迁移 k 次。每次「迁移」操作将会引发下述活动:
- 位于 grid[i][j] 的元素将会移动到 grid[i][j + 1]。
- 位于 grid[i][n - 1] 的元素将会移动到 grid[i + 1][0]。
- 位于 grid[m - 1][n - 1] 的元素将会移动到 grid[0][0]。
- 请你返回 k 次迁移操作后最终得到的 二维网格。
例如:
输入:grid = [[1,2,3],[4,5,6],[7,8,9]], k = 1
输出:[[9,1,2],[3,4,5],[6,7,8]]
解题思路:按照题意一步一步做有些费劲,需要使用两个 for 循环,计算量少一些不够更加费时,如方法(一),使用贪心找规律计算量稍微大一些,但委实快了许多。
方法(一)
class Solution:
def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]:
# 自己写的 没找规律
# GF 找规律的想法确实快多了
m,n = len(grid),len(grid[0])
if k == 0: return grid
if m == 1 and n == 1: return grid
ans = [[0 for _ in range(n)] for _ in range(m)]
while k > 0:
ans[0][0] = grid[m-1][n-1]
if m > 1:
for i in range(1,m):
ans[i][0] = grid[i-1][n-1]
if n > 1:
for i in range(m):
for j in range(1,n):
ans[i][j] = grid[i][j-1]
grid = [row[:] for row in ans ]
k -= 1
return ans
方法(二)
class Solution:
def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]:
m,n = len(grid),len(grid[0])
ans = [[0] * n for _ in range(m)]
for i,row in enumerate(grid):
for j,v in enumerate(row):
index1 = (i*n + j + k) % (m*n)
ans[index1 // n][index1 % n] = v
return ans
1266. 访问所有点的最小时间
题目链接:1266. 访问所有点的最小时间
题目大意:平面上有 n 个点,点的位置用整数坐标表示 points[i] = [xi, yi] 。请你计算访问所有这些点需要的 最小时间(以秒为单位)。你需要按照下面的规则在平面上移动:
- 每一秒内,你可以:
- 沿水平方向移动一个单位长度,或者
- 沿竖直方向移动一个单位长度,或者
- 跨过对角线移动 sqrt(2) 个单位长度(可以看作在一秒内向水平和竖直方向各移动一个单位长度)。
- 必须按照数组中出现的顺序来访问这些点。在访问某个点时,可以经过该点后面出现的点,但经过的那些点不算作有效访问。
例如:
输入:points = [[1,1],[3,4],[-1,0]]
输出:7
解释:一条最佳的访问路径是: [1,1] -> [2,2] -> [3,3] -> [3,4] -> [2,3] -> [1,2] -> [0,1] -> [-1,0]
从 [1,1] 到 [3,4] 需要 3 秒
从 [3,4] 到 [-1,0] 需要 4 秒
一共需要 7 秒
解题思路:
- 贪心,两个点之间 x、y的各自差值绝对值的对比。
class Solution:
def minTimeToVisitAllPoints(self, points: List[List[int]]) -> int:
n = len(points)
x1,y1 = points[0]
ans = 0
for i in range(1,n):
x2,y2 = points[i]
ans += max(abs(x1-x2),abs(y1-y2))
x1,y1 = x2,y2
return ans
1275. 找出井字棋的获胜者
题目链接:1275. 找出井字棋的获胜者
题目大意:A 和 B 在一个 3 x 3 的网格上玩井字棋。井字棋游戏的规则如下:
- 玩家轮流将棋子放在空方格 (" ") 上。第一个玩家 A 总是用 “X” 作为棋子,而第二个玩家 B 总是用 “O” 作为棋子。“X” 和 “O” 只能放在空方格中,而不能放在已经被占用的方格上。只要有 3 个相同的(非空)棋子排成一条直线(行、列、对角线)时,游戏结束。如果所有方块都放满棋子(不为空),游戏也会结束。游戏结束后,棋子无法再进行任何移动。给你一个数组 moves,其中每个元素是大小为 2 的另一个数组(元素分别对应网格的行和列),它按照 A 和 B 的行动顺序(先 A 后 B)记录了两人各自的棋子位置。
- 如果游戏存在获胜者(A 或 B),就返回该游戏的获胜者;如果游戏以平局结束,则返回 “Draw”;如果仍会有行动(游戏未结束),则返回 “Pending”。
- 你可以假设 moves 都 有效(遵循井字棋规则),网格最初是空的,A 将先行动。
例如:
输入:moves = [[0,0],[2,0],[1,1],[2,1],[2,2]]
输出:"A"
解释:"A" 获胜,他总是先走。
"X " "X " "X " "X " "X "
" " -> " " -> " X " -> " X " -> " X "
" " "O " "O " "OO " "OOX"
解题思路:
- 参考这篇题解写得代码 参考题解
- 主要思路 看代码了 很清晰了
- 这里需要备注一点 这里的 itertools.permutations(A,3) 太好用了
from itertools import permutations
class Solution:
def tictactoe(self, moves: List[List[int]]) -> str:
if len(moves)<5: return 'Pending'
A,B = moves[::2],moves[1::2]
for (x1,y1),(x2,y2),(x3,y3) in permutations(A,3):
if (y2-y1)*(x3-x1) == (y3-y1)*(x2-x1):
return 'A'
for (x1,y1),(x2,y2),(x3,y3) in permutations(B,3):
if (y2-y1)*(x3-x1) == (y3-y1)*(x2-x1):
return 'B'
return 'Draw' if len(A)+len(B) == 9 else 'Pending'
1287. 有序数组中出现次数超过25%的元素
题目链接:1287. 有序数组中出现次数超过25%的元素
题目大意:给你一个非递减的 有序 整数数组,已知这个数组中恰好有一个整数,它的出现次数超过数组元素总数的 25%。请你找到并返回这个整数.
例如:
输入:arr = [1,2,2,6,6,6,6,7,10]
输出:6
解题思路: 充分利用 有序数组的性质 即比较 arr[i] == arr[i+n//4]:
(一) 偷懒
class Solution:
def findSpecialInteger(self, arr: List[int]) -> int:
n = len(arr)
counter = collections.Counter(arr)
return counter.most_common()[0][0]
(二) 充分利用 有序数组的性质
class Solution:
def findSpecialInteger(self, arr: List[int]) -> int:
n = len(arr)
for i in range(n):
if arr[i] == arr[i+n//4]:
return arr[i]
return -1
1295. 统计位数为偶数的数字
题目链接:1295. 统计位数为偶数的数字
题目大意: 给你一个整数数组 nums,请你返回其中位数为 偶数 的数字的个数。
例如:
输入:nums = [12,345,2,6,7896]
输出:2
解释:
12 是 2 位数字(位数为偶数)
345 是 3 位数字(位数为奇数)
2 是 1 位数字(位数为奇数)
6 是 1 位数字 位数为奇数)
7896 是 4 位数字(位数为偶数)
因此只有 12 和 7896 是位数为偶数的数字
解题思路: 两种方法 如下:
(一)稍微笨一点的
class Solution:
def findNumbers(self, nums: List[int]) -> int:
ans = 0
for num in nums:
if 9 < num < 100 or 999 < num < 10000 or num == 100000:
ans += 1
return ans
(二)转化成字符串长度进行判断
class Solution:
def findNumbers(self, nums: List[int]) -> int:
return sum(1 for num in nums if len(str(num)) % 2 == 0)
1299. 将每个元素替换为右侧最大元素
题目链接:1299. 将每个元素替换为右侧最大元素
题目大意:给你一个数组 arr ,请你将每个元素用它右边最大的元素替换,如果是最后一个元素,用 -1 替换。完成所有替换操作后,请你返回这个数组。
例如:
输入:arr = [17,18,5,4,6,1]
输出:[18,6,6,6,1,-1]
解释:
- 下标 0 的元素 --> 右侧最大元素是下标 1 的元素 (18)
- 下标 1 的元素 --> 右侧最大元素是下标 4 的元素 (6)
- 下标 2 的元素 --> 右侧最大元素是下标 4 的元素 (6)
- 下标 3 的元素 --> 右侧最大元素是下标 4 的元素 (6)
- 下标 4 的元素 --> 右侧最大元素是下标 5 的元素 (1)
- 下标 5 的元素 --> 右侧没有其他元素,替换为 -1
解题思路 :
- 设置两个变量
- 逆序遍历 边走边赋值 同时更新最大变量
class Solution:
def replaceElements(self, arr: List[int]) -> List[int]:
# 逆序遍历
n = len(arr)
j,tmp = -1,0
for i in range(n-1,-1,-1):
tmp = arr[i]
arr[i] = j
j = max(j,tmp)
return arr
1300. 转变数组后最接近目标值的数组和 **
题目链接:1300. 转变数组后最接近目标值的数组和
题目大意: 给你一个整数数组 arr 和一个目标值 target ,请你返回一个整数 value ,使得将数组中所有大于 value 的值变成 value 后,数组的和最接近 target (最接近表示两者之差的绝对值最小)。如果有多种使得和最接近 target 的方案,请你返回这些整数中的最小值。
- 请注意,答案不一定是 arr 中的数字。
例如:
输入:arr = [4,9,3], target = 10
输出:3
解释:当选择 value 为 3 时,数组会变成 [3, 3, 3],和为 9 ,这是最接近 target 的方案。
解题思路: 二分查找 有点难 需要再想想。
class Solution:
def findBestValue(self, arr: List[int], target: int) -> int:
low,high = 0,max(arr)
if sum(arr) <= target: return high
def check(x: int) -> int:
res = 0
for num in arr:
res += num if num <= x else x
return res
def find_smaller(L: int,R: int) -> int:
while L+1 < R:
mid = L + (R-L)//2
if check(mid) <= target:
L = mid
else:
R = mid
return R if check(R) < target else L
def find_larger(L: int,R: int) -> int:
while L+1 < R:
mid = L + (R-L)//2
if check(mid) >= target:
R = mid
else:
L = mid
return L if check(L) > target else R
smaller = find_smaller(low,high)
larger = find_larger(low,high)
if abs(target-check(smaller))<=abs(target-check(larger)):
return smaller
else:
return larger
1304. 和为零的 N 个不同整数
题目链接:1304. 和为零的 N 个不同整数
题目大意: 给你一个整数 n,请你返回 任意 一个由 n 个 各不相同 的整数组成的数组,并且这 n 个数相加和为 0 。
例如:
输入:n = 5
输出:[-7,-1,1,3,4]
解释:这些数组也是正确的 [-5,-1,1,2,3],[-3,-1,2,-2,4]。
解题思路:简单题 这里提供两种解法
方法(一)
class Solution:
def sumZero(self, n: int) -> List[int]:
ans = [x for x in range(1,n)]
ans.append(-sum(ans))
return ans
方法(二)
class Solution:
def sumZero(self, n: int) -> List[int]:
ans = [x for x in range(1,n)]
ans.append(-sum(ans))
return ans
ans = [i for i in range(1,n//2+1)]
ans += [-i for i in ans]
if n % 2 == 0:
return ans
else:
return ans + [0]
1306. 跳跃游戏 III
题目链接:1306. 跳跃游戏 III
题目大意:这里有一个非负整数数组 arr,你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时,你可以跳到 i + arr[i] 或者 i - arr[i]。请你判断自己是否能够跳到对应元素值为 0 的 任一 下标处。注意,不管是什么情况下,你都无法跳到数组之外。
例如:
输入:arr = [4,2,3,0,3,1,2], start = 5
输出:true
解释:到达值为 0 的下标 3 有以下可能方案:
下标 5 -> 下标 4 -> 下标 1 -> 下标 3
下标 5 -> 下标 6 -> 下标 4 -> 下标 1 -> 下标 3
解题思路: 稍微复杂一些 这里仅提供 递归的思路 最好理解了!
class Solution:
def canReach(self, arr: List[int], start: int) -> bool:
n = len(arr)
if 0<= start <n and arr[start]<n:
tmp = arr[start]
if tmp == 0: return True
# print('tmp',tmp,'start',start)
arr[start] += n
return self.canReach(arr,start+tmp) | self.canReach(arr,start-tmp)
return False
1313. 解压缩编码列表
题目链接:1313. 解压缩编码列表
题目大意:给你一个以行程长度编码压缩的整数列表 nums 。考虑每对相邻的两个元素 [freq, val] = [nums[2i], nums[2i+1]] (其中 i >= 0 ),每一对都表示解压后子列表中有 freq 个值为 val 的元素,你需要从左到右连接所有子列表以生成解压后的列表。请你返回解压后的列表。
例如:
输入:nums = [1,2,3,4]
输出:[2,4,4,4]
解释:
第一对 [1,2] 代表着 2 的出现频次为 1,所以生成数组 [2]。
第二对 [3,4] 代表着 4 的出现频次为 3,所以生成数组 [4,4,4]。
最后将它们串联到一起 [2] + [4,4,4] = [2,4,4,4]。
解题思路: 有效的使用 py3 的数组切片功能
class Solution:
def decompressRLElist(self, nums: List[int]) -> List[int]:
a,b = nums[::2],nums[1::2]
ans = []
for i,j in zip(a,b):
ans += i*[j]
return ans
1317. 将整数转换为两个无零整数的和
题目链接:1317. 将整数转换为两个无零整数的和
题目大意:「无零整数」是十进制表示中 不含任何 0 的正整数。给你一个整数 n,请你返回一个 由两个整数组成的列表 [A, B],满足:
- A 和 B 都是无零整数
- A + B = n
- 题目数据保证至少有一个有效的解决方案。如果存在多个有效解决方案,你可以返回其中任意一个。
例如:
输入:n = 2
输出:[1,1]
解释:A = 1, B = 1. A + B = n 并且 A 和 B 的十进制表示形式都不包含任何 0 。
解题思路: 需要借助 字符串 的功能。
class Solution:
def getNoZeroIntegers(self, n: int) -> List[int]:
for L in range(n):
R = n-L
if '0' not in str(L) and '0' not in str(R):
return [L,R]
1380. 矩阵中的幸运数
题目链接:1380. 矩阵中的幸运数
题目大意:给你一个 m * n 的矩阵,矩阵中的数字 各不相同 。请你按 任意 顺序返回矩阵中的所有幸运数。幸运数 是指矩阵中满足同时下列两个条件的元素:
-
在同一行的所有元素中最小
-
在同一列的所有元素中最大
例如:
输入:matrix =
[[ 3, 7, 8],
[ 9,11,13],
[15,16,17]]
输出:[15]
解释:15 是唯一的幸运数,因为它是其所在行中的最小值,也是所在列中的最大值。
解题思路: 幸运数 只有一个 反证法证明
class Solution:
def luckyNumbers(self, matrix: List[List[int]]) -> List[int]:
a = max(min(row) for row in matrix)
b = min(max(col) for col in zip(*matrix))
return [a] if a==b else []
1385. 两个数组间的距离值
题目链接:1385. 两个数组间的距离值
题目大意:给你两个整数数组 arr1 , arr2 和一个整数 d ,请你返回两个数组之间的 距离值 。
「距离值」 定义为符合此距离要求的元素数目:对于元素 arr1[i] ,不存在任何元素 arr2[j] 满足 |arr1[i]-arr2[j]| <= d 。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/find-the-distance-value-between-two-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
例如:
输入:arr1 = [4,5,8], arr2 = [10,9,1,8], d = 2
输出:2
解释:
对于 arr1[0]=4 我们有:
|4-10|=6 > d=2 |4-9|=5 > d=2 |4-1|=3 > d=2 |4-8|=4 > d=2
所以 arr1[0]=4 符合距离要求
对于 arr1[1]=5 我们有:
|5-10|=5 > d=2 |5-9|=4 > d=2 |5-1|=4 > d=2 |5-8|=3 > d=2
所以 arr1[1]=5 也符合距离要求
对于 arr1[2]=8 我们有:
|8-10|=2 <= d=2 |8-9|=1 <= d=2 |8-1|=7 > d=2 |8-8|=0 <= d=2
存在距离小于等于 2 的情况,不符合距离要求
故而只有 arr1[0]=4 和 arr1[1]=5 两个符合距离要求,距离值为 2
解题思路: 二种方法,一种常规做法 双重循环;另一种 巧妙一些 二分法 减少运行时间
方法(一)
class Solution:
def findTheDistanceValue(self, arr1: List[int], arr2: List[int], d: int) -> int:
# arr1.sort()
n = len(arr1)
ans = 0
for i in range(n):
if all(abs(arr1[i]-num)>d for num in arr2) :
ans += 1
return ans
方法(二)
class Solution:
def findTheDistanceValue(self, arr1: List[int], arr2: List[int], d: int) -> int:
arr2.sort()
n = len(arr2)
ans = 0
for x in arr1:
p = bisect.bisect_left(arr2, x)
if p==n or abs(x-arr2[p])>d:
if p==0 or abs(x-arr2[p-1])>d:
ans += 1
# if 条件解释
# if p == n and abs(x - arr2[p - 1])> d:
# ans += 1
# if p == 0 and abs(x-arr2[p]) > d:
# ans += 1
# if 0< p < n and abs(x-arr2[p]) > d and abs(x-arr2[p-1])>d:
# ans += 1
return ans
1389. 按既定顺序创建目标数组
题目链接:1389. 按既定顺序创建目标数组
题目大意:给你两个整数数组 nums 和 index。你需要按照以下规则创建目标数组:
- 目标数组 target 最初为空。
- 按从左到右的顺序依次读取 nums[i] 和 index[i],在 target 数组中的下标 index[i] 处插入值 nums[i] 。
- 重复上一步,直到在 nums 和 index 中都没有要读取的元素。
- 请你返回目标数组,题目保证数字插入位置总是存在。
例如:
输入:nums = [0,1,2,3,4], index = [0,1,2,2,1]
输出:[0,4,1,3,2]
解释:
nums index target
0 0 [0]
1 1 [0,1]
2 2 [0,1,2]
3 2 [0,1,3,2]
4 1 [0,4,1,3,2]
解题思路: 充分利用数组切片的功能
class Solution:
def createTargetArray(self, nums: List[int], index: List[int]) -> List[int]:
ans = []
for num,i in zip(nums,index):
if i >= len(ans):
ans.append(num)
else:
ans = ans[:i] + [num] + ans[i:]
return ans
1464. 数组中两元素的最大乘积
题目链接:1464. 数组中两元素的最大乘积
题目大意:给你一个整数数组 nums,请你选择数组的两个不同下标 i 和 j,使 (nums[i]-1)*(nums[j]-1) 取得最大值。请你计算并返回该式的最大值。
例如:
输入:nums = [3,4,5,2]
输出:12
解释:如果选择下标 i=1 和 j=2(下标从 0 开始),则可以获得最大值,(nums[1]-1)*(nums[2]-1) = (4-1)*(5-1) = 3*4 = 12 。
解题思路: 双指针法 记录 最大值 和 次最大值 当然也可以选择排序直接获取 最大值和次最大值
class Solution:
def maxProduct(self, nums: List[int]) -> int:
n = len(nums)
if n == 2: return (nums[0]-1)*(nums[1]-1)
numMax,numSub = nums[0],nums[1]
if numMax < numSub: numMax,numSub = numSub,numMax
for i in range(2,n):
num = nums[i]
if num > numMax:
numMax,numSub = num,numMax
elif num > numSub:
numSub = num
return (numMax-1)*(numSub-1)
1470. 重新排列数组
题目链接:1470. 重新排列数组
题目大意:给你一个数组 nums ,数组中有 2n 个元素,按 [x1,x2,…,xn,y1,y2,…,yn] 的格式排列。请你将数组按 [x1,y1,x2,y2,…,xn,yn] 格式重新排列,返回重排后的数组。
例如:
输入:nums = [2,5,1,3,4,7], n = 3
输出:[2,3,5,4,1,7]
解释:由于 x1=2, x2=5, x3=1, y1=3, y2=4, y3=7 ,所以答案为 [2,3,5,4,1,7]
解题思路: 充分利用数组切片的功能
class Solution:
def shuffle(self, nums: List[int], n: int) -> List[int]:
ans = []
for i,j in zip(nums[:n],nums[n:]):
ans += [i,j]
return ans
总结
数组,可算弄完了,真累! 收获还是挺大的,线段树是个硬伤啊!必须解决了,它本身变量就有5个,而且代码贼长,与其他的方法结合又十分的让人脑壳疼,不过,我应该可以应付!虽然这次的主题是数组,但字符、动态规划、BFS、DFS、贪心、双指针、二分法、哈希表等都遇到了,只能说学无止境,继续刷题!最后,努力!奋斗!