LeetCode-算法:51-60(Python)
51. N皇后
思路
- 排除对应行,列和两个对角线的位置冲突:
- 所在的行(cur)列(col)在上面行已经有皇后
- 斜线上已存在皇后,斜线公式为
正斜线: y = x y=x y=x,负斜线: y = − x y=-x y=−x,则: y = ∣ x ∣ y=|x| y=∣x∣
x 1 : c o l , y 1 : c u r , x 2 : A [ r o w ] , y 2 : r o w x_1:col, y_1:cur, x_2:A[row], y_2:row x1:col,y1:cur,x2:A[row],y2:row
判断 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1, y_1), (x_2, y_2) (x1,y1),(x2,y2)是否在同一线上,则:
y 1 − y 2 = ∣ x 1 − x 2 ∣ y_1-y_2 = |x_1-x_2| y1−y2=∣x1−x2∣
回溯函数queen(cur):
- 从第一行cur = 0 开始
- 循环列col放置皇后
- 如果A[cur]不在攻击范围内(排除对应行,列和两个对角线的位置冲突)
- 在A[cur]方格上放置皇后
- 如果cur+1 == n,皇后填完,添加答案至ans中
- 否则寻找下一行的解,queen(cur+1)
- 回溯:A[cur]皇后位置清除
- 如果A[cur]不在攻击范围内(排除对应行,列和两个对角线的位置冲突)
class Solution(object):
def solveNQueens(self, n):
"""
:type n: int
:rtype: List[List[str]]
"""
def attack(cur, col):
for row in range(cur):
if A[row] == col or cur - row == abs(col - A[row]):
return True
return False
def queen(cur=0):
for col in range(n):
if not attack(cur, col):
A[cur] = col
if cur + 1 == n:
temp = list()
for i in range(n):
# 如当n=4时,第一个答案为[1, 3, 0, 2],按返回格式赋值
temp.append('.'*A[i]+'Q'+'.'*(n-A[i]-1))
ans.append(temp)
else:
queen(cur+1)
A[cur] = None
return
ans = list()
A = [None]*n
queen()
return ans
52. N皇后 II
思路
回溯函数queen(cur, count):
- 从第一行cur = 0 开始
- 循环列col放置皇后
- 如果A[cur]不在攻击范围内(排除对应行,列和两个对角线的位置)
- 在A[cur]方格上放置皇后
- 如果cur+1 == n,皇后填完,count +=1
- 否则寻找下一行的解,queen(cur+1, count)
- 回溯:A[cur]皇后位置清除
- 如果A[cur]不在攻击范围内(排除对应行,列和两个对角线的位置)
class Solution(object):
def totalNQueens(self, n):
"""
:type n: int
:rtype: int
"""
def attack(cur, col):
for row in range(cur):
if A[row] == col or cur - row == abs(col - A[row]):
return True
return False
def queen(cur=0, count=0):
for col in range(n):
if not attack(cur, col):
A[cur] = col
if cur + 1 == n:
count += 1
else:
count = queen(cur+1, count)
A[cur] = None
return count
A = [None]*n
return queen()
53. 最大子序和
思路
创建一个与数组等长的数组temp,记录nums对应位置的最大和,返回temp中的最大值
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
nlen = len(nums)
temp = [None]*nlen
temp[0] = nums[0]
i = 1
while i < nlen:
if temp[i-1] < 0: # 当最大和为负数时
temp[i] = nums[i]
else:
temp[i] = temp[i-1] + nums[i]
i += 1
return max(temp)
54. 螺旋矩阵
思路
- 当列表为空,返回[]
- 当列表只有一行,返回该行
- 当列表只有一列,返回该列
- 顺时针螺旋顺序添加元素至ans
- 创建与matrix大小一致的trail列表跟踪足迹
- dr为方向,0:向右(c+1),1:向下(r-1),2:向左(c-1),3.向上(r+1)
- 共有m*n个数,顺时针螺旋顺序添加matrix的元素至ans,已添加的位置trail[r][c]==True
- 顺时针螺旋顺序:
- 判断到边际的点(右上角(0, n-1),右下角(m-1, n-1),左下角(m-1, 0))或下一步是已走过的路(trail[r][c]==True)时改变方向
class Solution(object):
def spiralOrder(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: List[int]
"""
ans = list()
if not matrix:
return []
if len(matrix) == 1:
return matrix[0]
if len(matrix[0]) == 1:
for i in range(len(matrix)):
ans.append(matrix[i][0])
return ans
m = len(matrix) # row
n = len(matrix[0]) # column
trail = [[False]*n for _ in range(m)] # 未走的路为False,已走为True
r, c, dr = 0, 0, 0
for _ in range(m*n):
ans.append(matrix[r][c])
trail[r][c] = True
if dr%4 == 0:
c += 1
if c == n-1 or trail[r][c+1]:
dr += 1
elif dr%4 == 1:
r += 1
if r == m - 1 or trail[r+1][c]:
dr += 1
elif dr%4 == 2:
c -= 1
if c == 0 or trail[r][c-1]:
dr += 1
else:
r -= 1
if trail[r-1][c]:
dr += 1
return ans
55. 跳跃游戏
思路
贪心
- 初始化end,maxindex为0
- end为当前能到达的最大位置,maxindex在i到end途中时,寻找能到达的最大位置
- 当i==end时,更新end为maxindex
- 如果end能到达end走到等于nums或大于nums最后一个位置,则完成跳跃,否则,不能到达nums最后一个位置
class Solution(object):
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
if not nums or len(nums) == 1:
return True
nlen = len(nums)
end, maxindex = 0, 0
for i in range(nlen - 1):
maxindex = max(i + nums[i], maxindex)
if i == end:
end = maxindex
if end >= nlen - 1:
return True
return False
56. 合并区间
思路
- 根据intervals的第一个数进行排序
- 当ans=[]或interval第一个数大于ans里最后即最大的数时(如:ans[[1, 6], interval=[8, 10]),添加interval进ans中
- 当interval第一个数小于ans最后一个数时(如:ans=[1, 3],interval=[2, 6], 更新ans[-1][1]=interval[1]),更新ans最后的数
class Solution(object):
def merge(self, intervals):
"""
:type intervals: List[List[int]]
:rtype: List[List[int]]
"""
intervals.sort(key=lambda x:x[0])
ans = list()
for interval in intervals:
if not ans or interval[0] > ans[-1][1]:
ans.append(interval)
else:
ans[-1][1] = max(ans[-1][1], interval[1])
return ans
57. 插入区间
思路
将newInterval插入intervals中后,使用56题的方法进行合并区间
class Solution(object):
def insert(self, intervals, newInterval):
"""
:type intervals: List[List[int]]
:type newInterval: List[int]
:rtype: List[List[int]]
"""
intervals.append(newInterval)
intervals.sort(key=lambda x:x[0])
ans = list()
for interval in intervals:
if not ans or interval[0] > ans[-1][1]:
ans.append(interval)
else:
ans[-1][1] = max(ans[-1][1], interval[1])
return ans
58. 最后一个单词的长度
思路
- 去除字符串前后空格
- 从字符串后面开始计算,如果是字母长度加1,如果不是,结束循环
class Solution(object):
def lengthOfLastWord(self, s):
"""
:type s: str
:rtype: int
"""
s = s.strip()
end = len(s) - 1
count = 0
while end >= 0:
if s[end].isalpha():
count += 1
else:
break
end -= 1
return count
思路
- 去除字符串前后空格
- 以空格分隔的单词保存在ans中,返回最后一个单词的长度
class Solution(object):
def lengthOfLastWord(self, s):
"""
:type s: str
:rtype: int
"""
s = s.strip()
ans = s.split(' ')[-1]
return len(ans)
59. 螺旋矩阵 II
思路
- 当n=1,返回[[1]]
- 顺时针螺旋顺序添加正整数至ans
- 创建与matrix大小一致的trail列表跟踪足迹
- dr为方向,0:向右(c+1),1:向下(r-1),2:向左(c-1),3.向上(r+1)
- 共有n*n个数,顺时针螺旋顺序添加正整数至ans,已添加的位置trail[r][c]==True
- 顺时针螺旋顺序:
- 判断到边际的点(右上角(0, n-1),右下角(n-1, n-1),左下角(n-1, 0))或下一步是已走过的路(trail[r][c]==True)时改变方向
class Solution(object):
def generateMatrix(self, n):
"""
:type n: int
:rtype: List[List[int]]
"""
if n == 1:
return [[1]]
ans = [[0]*n for _ in range(n)]
trail = [[False]*n for _ in range(n)]
r, c, dr, tar = 0, 0, 0, n*n+1
for num in range(1, tar):
ans[r][c] = num
trail[r][c] = True
if dr%4 == 0: # 向右
c += 1
if c == n - 1 or trail[r][c+1]:
dr += 1
elif dr%4 == 1: # 向下
r += 1
if r == n - 1 or trail[r+1][c]:
dr += 1
elif dr%4 == 2: # 向左
c -= 1
if c == 0 or trail[r][c-1]:
dr += 1
else: # 向上
r -= 1
if trail[r-1][c]:
dr += 1
return ans
思路
- 从左到右添加正整数,添加一行后,往下移动一行
- 从上往下添加正整数,添加一列后,往左移动一列
- 从右往左添加正整数,添加一行后,往上移动一行
- 从下往上添加正整数,添加一列后,往右移动一列
class Solution(object):
def generateMatrix(self, n):
"""
:type n: int
:rtype: List[List[int]]
"""
ans = [[0]*n for _ in range(n)]
l, r, u, d, num, tar = 0, n-1, 0, n-1, 1, n*n+1
while num < tar:
for i in range(l, r+1): # 从左到右
ans[u][i] = num
num += 1
u += 1 # 往下移动一行
for i in range(u, d+1): # 从上往下
ans[i][r] = num
num += 1
r -= 1 # 往左移动一列
for i in range(r, l-1, -1): # 从右往左
ans[d][i] = num
num += 1
d -= 1 # 往上移动一行
for i in range(d, u-1, -1): # 从下往上
ans[i][l] = num
num += 1
l += 1 # 往右移动一列
return ans
60. 第k个排列
思路
- 全序列的排序其实和阶乘是一样的,创建一个阶乘函数factorial(n)
- 创建集合nums = [1, 2, 3, …, n]
- 第一位数在n个数中选取,每位数后面有(n-1)!种排序。第二位在n-1个数中选取,每位数后面有(n-2)!种排序…因此全序列有n*(n-1)*(n-2)!=n!种排序。因为index由0开始,根据第k个排序选取nums中的数字,因此k=k-1。如n=3,k=2,共有n!=3!=6种排序,第0个序列为123(k=1),第1个序列为132(k=2)…
- 选择第一位数, index=k//(n-1)!=1//(3-1)!=1//2=0,选取nums[0]=1。因为已选取第一位数,位置已经移位至第index*(n-1)!,所以更新k=k-index*(n-1)=1-0*(3-1)!=1。k=k%(n-1)!=1%(3-1)!=1得到的结果一样。将第一位数添加至 ans=[1]。nums中去除已选的第一位数得到nums=[2, 3]。nums剩下2位数,更新n=n-1=3-1=2。
- 选择第二位数,index=k//(n-1)!=1//(2-1)!=1,选取nums[1]=3。更新k=k%(n-1)!=1%(2-1)!=0,ans=[1, 3],nums=[2],n=n-1=2-1=1。
- 选取第三位数,index=k//(n-1)!=0//(1-1)!=0,选取nums[0]=2。更新k=k%(n-1)!=0%(1-1)!=0,ans=[1, 3, 2],nums=[],n=n-1=1-1=0,结束循环,返回ans。
k | 1 | 2 | 3 |
---|---|---|---|
1 | 1 | 2 | 3 |
2 | 1 | 3 | 2 |
3 | 2 | 1 | 3 |
4 | 2 | 3 | 1 |
5 | 3 | 1 | 2 |
6 | 3 | 2 | 1 |
class Solution(object):
def getPermutation(self, n, k):
"""
:type n: int
:type k: int
:rtype: str
"""
def factorial(n):
if n == 0 or n == 1:
return 1
temp = 1
for i in range(2, n+1):
temp *= i
return temp
nums = [x for x in range(1, n+1)]
ans = ""
k -= 1
while n > 0:
fac = factorial(n-1)
index = k//fac
k %= fac
ans += str(nums[index])
nums = nums[0:index] + nums[index+1:]
n -= 1
return ans