leetcode 51~60 学习经历
- 51. N 皇后
- 52. N 皇后 II
- 53. 最大子数组和
- 54. 螺旋矩阵
- 55. 跳跃游戏
- 56. 合并区间
- 57. 插入区间
- 58. 最后一个单词的长度
- 59. 螺旋矩阵 II
- 60. 排列序列
- 小结
51. N 皇后
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例 1:
输入:n = 4
输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1
输出:[[“Q”]]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/n-queens
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
额额。。。这咋弄,暴力尝试吧
class Solution:
def solveNQueens(self, n: int) -> list[list[str]]:
r = []
def findAnswer(n,m,arr,u):
for i in range(n):
dp = [[x for x in row] for row in arr]
ready = u
if dp[m][i] == ' ':
dp[m][i] = 'Q'
ready += 1
if ready == n:
r.append([''.join([x if x == 'Q' else '.' for x in row]) for row in dp])
for j in range(1,n - m):
dp[m + j][i] = 'X'
if i + j < n:
dp[m + j][i + j] = 'X'
if i - j >= 0:
dp[m + j][i - j] = 'X'
if m < n - 1:
findAnswer(n,m + 1,dp,ready)
findAnswer(n,0,[[' ' for _ in range(n)] for _ in range(n)],0)
return r
这次是这么个路子,每行放1个,放下之后,剩余行部分地方就不能放了,我用X来占领,并记录已经放成功几个,当放成功的数量和 n 相等,记录这个放法
然后调整了两个判断条件,不符合就提前跳出
class Solution:
def solveNQueens(self, n: int) -> list[list[str]]:
r = []
def findAnswer(n,m,arr,u):
if arr[m].count('') == 0:
return
for i in range(n):
if arr[m][i] != '':
continue
dp = [[x for x in row] for row in arr]
dp[m][i] = 'Q'
ready = u + 1
if ready == n:
r.append([''.join([x if x == 'Q' else '.' for x in row]) for row in dp])
for j in range(1,n - m):
dp[m + j][i] = '.'
if i + j < n:
dp[m + j][i + j] = '.'
if i - j >= 0:
dp[m + j][i - j] = '.'
if m < n - 1:
findAnswer(n,m + 1,dp,ready)
findAnswer(n,0,[['' for _ in range(n)] for _ in range(n)],0)
return r
然后就抓瞎了,才刚及格的排名,抄答案去
然后就是蚊香眼。。。。大佬们都是用数字表示状态,根本没用矩阵、字符之类的状态。。。厉害了
试试能不能修改下自己的代码
class Solution:
def solveNQueens(self, n: int) -> list[list[str]]:
r = []
def findAnswer(n,arr,s):
row = len(arr)
if row == n:
r.append(['.' * arr[x] + 'Q' + (n - arr[x] - 1) * '.' for x in range(n)])
return
for i in range(n):
if i in arr or row * 10 + i in s:
continue
u = set()
for x in range(1,n - row):
if i + x < n:
u.add((row+x) * 10 + i + x)
if i - x >= 0:
u.add((row+x) * 10 + i - x)
findAnswer(n, arr + [i], s.union(u))
findAnswer(n,[],set())
return r
大佬关于判断位置可用的部分没看懂,还是保留了自己的不可用判定,效率还是没达到大佬的程度啊
52. N 皇后 II
n 皇后问题 研究的是如何将 n 个皇后放置在 n × n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。
示例 1:
输入:n = 4
输出:2
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1
输出:1
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/n-queens-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
和51一样的题目,就是一个输出方案详情,一个输出方案数量,拿来改改先用着
class Solution:
def totalNQueens(self, n: int) -> int:
def findAnswer(n,arr,s):
r = 0
row = len(arr)
if row == n:
return 1
for i in range(n):
if i in arr or row * 10 + i in s:
continue
u = set()
for x in range(1,n - row):
if i + x < n:
u.add((row+x) * 10 + i + x)
if i - x >= 0:
u.add((row+x) * 10 + i - x)
r += findAnswer(n, arr + [i], s.union(u))
return r
return findAnswer(n,[],set())
然后又看了看大佬们的答案,40ms的和天书一样了。。。至于更前面的,are you kidding me?
看来这种得出单一数字答案的题目,除非用例够多,否则就这么几个十几个用例,直接答案给你。。。
53. 最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/maximum-subarray
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
https://blog.csdn.net/superwfei/article/details/128983274?spm=1001.2014.3001.5502,这是之前老顾用二维数组的做法,结果把这个办法放到 leetcode,直接超时了。。。
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
l = len(nums)
mx = nums[0]
dp = [[0]*(l+1) for _ in range(l)]
dp[0][0] = nums[0]
for i in range(1,l):
dp[i][0] = nums[i]
for j in range(i):
dp[i][j+1] = nums[i] + dp[i-1][j]
mx = max(mx,dp[i][j+1],dp[i][0])
return mx
因为这次不求坐标,所以这个办法估计是真不适用了
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
mx = 0
mxi = nums[0]
s = 0
for i in range(len(nums)):
mxi = max(mxi,nums[i])
s += nums[i]
if s < 0:
s = 0
if mxi > 0:
mx = max(mx,s)
return mx if mxi > 0 else mxi
换了个顺序加法的,结果成绩不理想,另外,分治法是啥?先不管,再删一个变量,提前预判一下结果
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
mx,s = max(nums),0
if mx <= 0:
return mx
for i in range(len(nums)):
s += nums[i]
if s < 0:
s = 0
continue
mx = max(mx,s)
return mx
现在,可以去抄答案了
有看没有懂,有了方针没有方案。。。。。抄错地方了
# 以下内容抄自 leetcode 第53题 python 68ms 答案
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
max = nums[0]
sum = 0
for i in nums:
if sum <= 0 :
sum = i
else :
sum += i
if sum > max:
max = sum
return max
也没有分治法啊?啥情况?这个是运算中判断比我少一些,看代码看不出来,逻辑上少了一些。。。
54. 螺旋矩阵
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/spiral-matrix
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
老顾天真了,开始以为都是 m * n 的连续自然数,结果中间踩坑了,甚至矩阵里还有相同的数字。。。。
哎。。。。提交了三次才正确
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
arr = []
t = 0
while len(matrix) > 0:
if t % 4 == 0:
arr += matrix[0]
matrix.pop(0)
elif t % 4 == 1:
for j in range(len(matrix)):
arr.append(matrix[j][-1])
matrix[j].pop(-1)
elif t % 4 == 2:
arr += matrix[-1][::-1]
matrix.pop(-1)
else:
for j in range(len(matrix),0,-1):
arr.append(matrix[j - 1][0])
matrix[j - 1].pop(0)
if len(matrix) == 0 or len(matrix[0]) == 0:
break
t += 1
return arr
#以下内容抄自 leetcode 54题 python 16ms 答案
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
up,down,left,right = 0,len(matrix)-1,0,len(matrix[0])-1
ans = []
while len(ans) < len(matrix)*len(matrix[0]):
if up <= down:
for i in range(left,right+1):
ans.append(matrix[up][i])
up = up + 1
if left <= right:
for i in range(up,down+1):
ans.append(matrix[i][right])
right = right - 1
if up <= down:
for i in range(right,left-1,-1):
ans.append(matrix[down][i])
down = down - 1
if left <= right:
for i in range(down,up-1,-1):
ans.append(matrix[i][left])
left = left + 1
return ans
嗯,也是记录方向,不过没有对原数组操作,相当于4指针。。。。巧妙啊
55. 跳跃游戏
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/jump-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这个题好熟悉,好像做过!第45题!这就和 N皇后,N皇后2是同一个内容,返回不同结果一样,没啥说的,调整下就提交
class Solution:
def canJump(self, nums: List[int]) -> bool:
n = len(nums)
if n < 2:
return True
pos = 0
while True:
if pos + nums[pos] >= n - 1:
return True
if nums[pos] == 0:
return False
mx,nxt = 0,0
for i in range(nums[pos],-1,-1):
if i + nums[pos + i] > mx:
mx = i + nums[pos + i]
nxt = i
pos += nxt
56. 合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/merge-intervals
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这个就没必要用交集了吧,排序后合并就好
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
if len(intervals) == 0:
return []
intervals.sort()
r = [intervals[0]]
for i in range(1,len(intervals)):
if intervals[i][0] <= r[-1][1]:
r[-1][1] = max(intervals[i][1],r[-1][1])
else:
r.append(intervals[i])
return r
# 以下内容抄自 leetcode第56题 python 32ms答案
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals = sorted(intervals, key=lambda x: x[0])
ans = [intervals[0]]
for i in range(1, len(intervals)):
tmp = ans[-1]
if tmp[1] < intervals[i][0]:
ans.append(intervals[i])
elif tmp[1] < intervals[i][1]:
tmp[1] = intervals[i][1]
return ans
嗯,减少了一次判断赋值。。。。
57. 插入区间
给你一个 无重叠的 ,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。
示例 1:
输入:intervals = [[1,3],[6,9]], newInterval = [2,5]
输出:[[1,5],[6,9]]
示例 2:
输入:intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
输出:[[1,2],[3,10],[12,16]]
解释:这是因为新的区间 [4,8] 与 [3,5],[6,7],[8,10] 重叠。
示例 3:
输入:intervals = [], newInterval = [5,7]
输出:[[5,7]]
示例 4:
输入:intervals = [[1,5]], newInterval = [2,3]
输出:[[1,5]]
示例 5:
输入:intervals = [[1,5]], newInterval = [2,7]
输出:[[1,7]]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/insert-interval
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
额。。。无重叠的是重点,不过先忽略他,按上一题做法做一次
class Solution:
def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
n = len(intervals)
if n == 0:
return [newInterval]
intervals = sorted(intervals + [newInterval])
r = [intervals[0]]
for i in range(1,n + 1):
if intervals[i][0] > r[-1][1]:
r.append(intervals[i])
elif intervals[i][1] > r[-1][1]:
r[-1][1] = intervals[i][1]
return r
然后,无重叠,写了个自行插入的。。。效率有点不尽如人意,还是再去抄一抄吧。。。
然后,被24ms大佬用类似指针的操作秀了一脸。。。
58. 最后一个单词的长度
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/length-of-last-word
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
嗯。。随便用 split 测了一下,他中间应该没有出现 school-bag 这样的然后要求返回 bag 长度的情况
class Solution:
def lengthOfLastWord(self, s: str) -> int:
return len(s.split()[-1])
就算是用循环,从后向前读字符,也不会浪费多少时间
class Solution:
def lengthOfLastWord(self, s: str) -> int:
r = ''
for i in range(len(s) - 1,-1,-1):
if len(r) > 0 and s[i] == ' ':
break
if s[i] != ' ':
r += s[i]
return len(r)
没什么好说的
59. 螺旋矩阵 II
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
示例 1:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:
输入:n = 1
输出:[[1]]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/spiral-matrix-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这次是我自己控制矩阵了吧,不用再考虑什么重复啦,方向啦
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
dp = [[0] * n for _ in range(n)]
x,y,row,col = 0,0,0,1
for i in range(n ** 2):
dp[x][y] = i + 1
x += row
y += col
if col == 1 and (y + col >= n or dp[x][y + col] != 0):
row,col = 1,0
if col == -1 and (y + col < 0 or dp[x][y + col] != 0):
row,col = -1,0
if row == 1 and (x + row >= n or dp[x + row][y] != 0):
row,col = 0,-1
if row == -1 and (x + row < 0 or dp[x + row][y] != 0):
row,col = 0,1
return dp
四个指针的办法,就不用了,咱这个就很暴力,很顺滑了
60. 排列序列
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
“123”
“132”
“213”
“231”
“312”
“321”
给定 n 和 k,返回第 k 个排列。
示例 1:
输入:n = 3, k = 3
输出:“213”
示例 2:
输入:n = 4, k = 9
输出:“2314”
示例 3:
输入:n = 3, k = 1
输出:“123”
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/permutation-sequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
啊, 啊啊,想起来第 31 题,下一个排列,当时就把我弄的有点蒙,嗯这次更好,不问下一个了,直接问,从第一个开始,下边第n个是啥排列,就不抄自己以前额答案了,从新写个看看有没有进步
class Solution:
def getPermutation(self, n: int, k: int) -> str:
r = [_ + 1 for _ in range(n)]
def move(arr):
n = len(arr)
for i in range(n - 1,0,-1):
l = arr[i - 1]
c = arr[i]
r = arr[i + 1:]
if c > l:
if len(r) == 0:
arr[i],arr[i - 1] = arr[i - 1],arr[i]
return arr[:i] + sorted(arr[i:])
k = [k for k in r if k > l]
if len(k) == 0:
arr[i],arr[i - 1] = arr[i - 1],arr[i]
return arr[:i] + sorted(arr[i:])
if len(k) > 0:
mn = min(k)
return arr[:i - 1] + [mn] + sorted([n for n in arr[i - 1:] if n != mn])
arr.sort()
return arr
for i in range(1,k):
r = move(r)
return ''.join([str(i) for i in r])
我去。。。。正确率倒是有了,效率这么低?
class Solution:
def getPermutation(self, n: int, k: int) -> str:
#n,k=4,9
r = [_ + 1 for _ in range(n)]
def P(n):
if n < 2:
return 1
return n * P(n - 1)
def move(arr):
n = len(arr)
for i in range(n - 1,0,-1):
l = arr[i - 1]
c = arr[i]
r = arr[i + 1:]
if c > l:
if len(r) == 0:
arr[i],arr[i - 1] = arr[i - 1],arr[i]
return arr[:i] + sorted(arr[i:])
k = [k for k in r if k > l]
if len(k) == 0:
arr[i],arr[i - 1] = arr[i - 1],arr[i]
return arr[:i] + sorted(arr[i:])
if len(k) > 0:
mn = min(k)
return arr[:i - 1] + [mn] + sorted([n for n in arr[i - 1:] if n != mn])
arr.sort()
return arr
k = k % P(n) if k % P(n) != 0 else k
res = 0
for i in range(1,n):
if P(i + 1) <= k:
res = i + 1
else:
break
print(res,P(res))
k -= P(res) - 1
r = r[:n - res] + r[n - res:][::-1]
for i in range(1,k):
r = move(r)
return ''.join([str(i) for i in r])
加了一个阶乘后,效率好了一点,那么,就把可以用递归的全部用递归完成
class Solution:
def getPermutation(self, n: int, k: int) -> str:
if n == 1:
return '1'
if n == 2:
return '12' if k % 2 == 1 else '21'
k -= 1
r = [_ + 1 for _ in range(n)]
def P(n):
if n < 2:
return 1
return n * P(n - 1)
for i in range(n,0,-1):
if k >= P(i):
p = k // P(i)
r[-i - 1],r[-i - 1 + p] = r[-i - 1 + p],r[-i - 1]
r = r[:-i] + sorted(r[-i:])
k = k % P(i)
return ''.join([str(i) for i in r])
这里用阶乘代替了数字移动。。。比如4个数,初始是1234,这个对应的是1,然后移动2次,就是2的阶乘1次,得到的就是1324,如果是移动4次,就是2的阶乘两次,那就是1423
1234 0 k == 1
1243 1 k == 2
1324 2
1342 3
1423 4
2134 6
3124 12
4123 18
可以发现规律,那就是阶乘次数表示多少个数字移动,2的阶乘移动3个数字,5的阶乘移动6个数字,至于移动哪个数字,看当前移动次数整除阶乘后的结果,所以。。。阶乘用来计算这个太方便了
# 以下内容抄自 leetcode 第60题 python 20ms 答案
class Solution:
def getPermutation(self, n: int, k: int) -> str:
l = [str(i) for i in range(1, n + 1)]
d = ''
for i in range(n):
nn = factorial((n - 1))
d += l.pop(ceil(k / nn) - 1)
k = k % nn
print(k)
n -= 1
return d
额。。。factorial 是阶乘的意思。。。在哪个包里?这个大佬更巧妙的根据阶乘 pop 掉某个位置的数字,用来拼接成字符串
小结
这次这10题不算难,第60题需要考虑到最大组合就是阶乘数量,根据这个就可以方便的得到结果了,如果想不到。。。那就真抓瞎了。
慢慢刷题,点点积累,日日进步