71、简化路径
以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。
在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (…) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径
请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。
解:用栈存储,’…‘则pop,’.‘和’/'则不管,遇到单词则加入。最后再把stack中的元素连接到一起。
class Solution:
def simplifyPath(self, path: str) -> str:
path=path.strip('/').split('/')
n=len(path)
stack=[]
for s in path:
if s=='..':
if stack:
stack.pop()
elif s=='.' or s=='':
pass
else:
stack.append(s)
return '/'+'/'.join(stack)
执行用时:40 ms, 在所有 Python3 提交中击败了84.89%的用户
内存消耗:13.5 MB, 在所有 Python3 提交中击败了89.08%的用户
72、编辑距离
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
解法一:该题最先想到的方法就是BFS,只要进行一定程度的遍历就能找到结果,但是该方法会超时,而且很难进行改进,在这里也不提了。
解法二:字符串相关问题一定要想到动态规划。该题是无法用一维动态规划去解决的,因此需要用二维动态规划去解决。关键是状态转移如何去解决。
令dp[i][j]为word1的前i个与word2的前j个字母匹配的最少操作次数。
这里首次要理解的是三种操作分别作用再1和2的情况
在word1中插入一个字符相当于在word2中删除一个字符。
在word1中删除等价于在word2中插入
在word1中替换相当于在word2中替换
由上述分析我们可以将6中操作化为3种操作,在word1中插入,在word2中插入,在word1中替换。这样的话我们每进行一次操作单词数量不会减少,这是可以进行动态规划的基础。
现在考虑递归表达式,对dp[i][j]考虑word1和word2的最后一个字母,如果相同,我们可以不做操作,直接处理dp[i-1][j-1],如果不相同势必要进行上述三种操作之一,如果是word1最后插入一个相同的,则变为dp[i][j-1],在word2插入则变为dp[i-1][j],在word1中替换,则变成dp[i-1][j-1].
综上:if word1[i-1]==word2[j-1],dp[i][j]=dp[i-1][j-1],
否则dp[i][j]=1+min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])。
最后再讨论初值dp[0][0]=0,dp[i][0]=i,dp[0][j]=j
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
n = len(word1)
m = len(word2)
if n * m == 0:
return n + m
dp=[[0 for _ in range(m+1)] for _ in range(n + 1)]
for i in range(n + 1):
dp[i][0] = i
for j in range(m + 1):
dp[0][j] = j
for i in range(1, n + 1):
for j in range(1, m + 1):
if word1[i-1]!=word2[j-1]:
dp[i][j]=1+min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])
else:
dp[i][j]=dp[i-1][j-1]
return dp[n][m]
执行用时:204 ms, 在所有 Python3 提交中击败了66.16%的用户
内存消耗:17.5 MB, 在所有 Python3 提交中击败了12.68%的用户
73、矩阵置零
给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。
解法一:朴素的想法就是先记录再去变零,就是这样的话效率还可以接受
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
m=len(matrix)
n=len(matrix[0])
col=[]
row=[]
for i in range(m):
haveZero=False
for j in range(n):
if matrix[i][j]==0:
if j not in col:
col.append(j)
if not haveZero:
haveZero=True
row.append(i)
for i in range(m):
if i in row:
matrix[i]=[0 for _ in range(n)]
else:
for j in range(n):
if j in col:
matrix[i][j]=0
执行用时:52 ms, 在所有 Python3 提交中击败了67.59%的用户
内存消耗:14.1 MB, 在所有 Python3 提交中击败了61.13%的用户
74、搜索二维矩阵
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
解:高效的话肯定要用二分法的思路来进行。把矩阵拉长成为一个数组,用二分法即可。
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
if not matrix or not matrix[0]:
return False
m=len(matrix)
n=len(matrix[0])
left=0
right=m*n-1
while left<right:
mid=(right+left)//2
a=matrix[mid//n][mid%n]
if a==target:
return True
elif a<target:
if mid+1>right:
return False
else:
left=mid+1
else:
if mid-1<left:
return False
else:
right=mid-1
if matrix[left//n][left%n]==target:
return True
else:
return False
执行用时:40 ms, 在所有 Python3 提交中击败了77.88%的用户
内存消耗:14.5 MB, 在所有 Python3 提交中击败了83.38%的用户
75、颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。
解法一:原地排序如果想要快一点可以用归并排序,但是归并排序需要O(n)的额外空间,并不是一个好的选择,这里还是写了下来。
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
def mergeSort(nums):
if len(nums)>1:
mid=len(nums)//2
lefthalf=nums[:mid]
righthalf=nums[mid:]
mergeSort(lefthalf)
mergeSort(righthalf)
i=j=k=0
while i<mid and j<len(nums)-mid:#拉链式插入
if lefthalf[i]<righthalf[j]:
nums[k]=lefthalf[i]
k=k+1
i=i+1
else:
nums[k]=righthalf[j]
k=k+1
j=j+1
while i<len(lefthalf):
nums[k]=lefthalf[i]
i=i+1
k=k+1
while j<len(righthalf):
nums[k]=righthalf[j]
j=j+1
k=k+1
mergeSort(nums)
执行用时:36 ms, 在所有 Python3 提交中击败了88.83%的用户
内存消耗:13.7 MB, 在所有 Python3 提交中击败了22.55%的用户
可以看出归并排序的速度确实快,但内存占用偏高。
解法二:事实上由于本题的特殊性(一共就三种不同的数),用双指针进行遍历和交换可以达到O(n)的速度,且只要常数级的存储空间。左指针的左侧是全零区域(遇到零可以和左指针的数交换),右指针的右侧是全二的区域,中间是待查区域。只是这样遇到1就很难处理,因此可以用第三个指针负责遍历,之前的两个指针纯粹用来标记边界。
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
i=k=0
n=len(nums)
if n==0:
return []
j=n-1
while k<=j:
if nums[k]==0:
if k!=i:
temp=nums[k]
nums[k]=nums[i]
nums[i]=temp
k=k+1
i=i+1
elif nums[k]==2:#j和它交换来的可能是0,因此k不能向前走,而i和k交换来已经被扫过,不含1和2,因此k可以向前走
temp=nums[k]
nums[k]=nums[j]
nums[j]=temp
j=j-1
else:
k=k+1
执行用时:36 ms, 在所有 Python3 提交中击败了88.83%的用户
内存消耗:13.7 MB在所有 Python3 提交中击败了26.55%的用户