刷题笔记
6.23
力扣62:不同路径
自己:使用了回溯法,超时,因为会有很多重复调用,可以加一个缓存,代码如下:(递归从下到上)
class Solution(object):
def uniquePaths(self, m, n):
d = {}
def dfs(i, j):
# 如果(i,j)在缓存中则直接返回
if (i, j) in d:
return d[i,j]
# 到达边界时,返回 1
if i == m - 1 or j == n - 1:
return 1
# 继续递归调用,往下i+1,往右j+1
d[i,j] = dfs(i + 1, j) + dfs(i, j + 1)
return d[i,j]
return dfs(0,0)
解法二:动态规划(从上到下)
状态转移方程:d[i][j]=d[i-1][j]+d[i][j-1]
首先处理边界问题,第一行第一列赋值1
class Solution(object):
def uniquePaths(self, m, n):
#先列再行
dp = [[0 for _ in range(n)] for _ in range(m)]
# 第一行都赋予 1
for j in range(n):
dp[0][j] = 1
# 第一列都赋予 1
for i in range(m):
dp[i][0] = 1
# 两个for循环推导,对于(i,j)来说,只能由上方或者左方转移过来
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[-1][-1]
解法三:优化解法二,动态规划中每一个需要创建一个二维数组的解法,都可以换成只创建一个一维数组的滚动数组解法,依据的规则是一般二维数组中存放的是所有的结果,但是一般我们需要的结果实在二维数组的最后一行的某个值,前面几行的值都是为了得到最后一行的值而需要的,所以可以开始就创建跟二维数组最后一行一样大的一维数组,每次存放某一行的值,下一次根据这一行的值算出下一行的值,在存入这个数组,也就是把这个数组滚动了,最后数组存储的结果就是原二维数组中最后一行的值。
所以申请一个1*n的数组,转移方程变为** dp[j]=dp[j]+dp[j-1]**
class Solution(object):
def uniquePaths(self, m, n):
dp = [1] *n
for i in range(1,m):
for i in range(1,n):
dp[j]=dp[j]+dp[j-1]
return dp[-1]
力扣39 组合总数
自己的解法:回溯(时间复杂度,空间复杂度高)
class Solution:
def combinationSum(self, candidates: List[int], target: int):
def dfs(candidates,target,size,res,ans,path):
if res == target:
ans.add(tuple(sorted(path)))
return
for i in range(size):
if target-res>=candidates[i]:
res = res+candidates[i]
path.append(candidates[i])
dfs(candidates,target,size,res,ans,path)
path.pop()
res = res-candidates[i]
size=len(candidates)
res=0
ans=set()
dfs(candidates,target,size,res,ans,[])
return list(ans)
刘老师今日课堂
- .list不能hash,即set.add(list)错误
- list.sort() 与sorted(list)区别,前者无返回值,后者返回一个可迭代的对象
正确的解法:回溯+剪枝
从target开始减去当前数字
剪枝:设置一个递归开始位置
剪枝小技巧
排列问题,讲究顺序(即 [2, 2, 3] 与 [2, 3, 2] 视为不同列表时),需要记录哪些数字已经使用过,此时用 used 数组,比如全排列
组合问题,不讲究顺序(即 [2, 2, 3] 与 [2, 3, 2] 视为相同列表时),需要按照某种顺序搜索,此时使用 begin 变量,比如这道题目
class Solution:
def combinationSum(self, candidates: List[int], target: int):
def dfs(candidates, begin,target, size, ans, path):
if target < 0:
return
if target == 0:
ans.append(path)
for i in range(begin,size):
dfs(candidates,i,target-candidates[i],size,ans,path+[candidates[i]])
size = len(candidates)
if size == 0:
return []
ans = []
dfs(candidates, 0,target, size, ans, [])
return ans
6.24刷题
力扣14 最长公共前缀
我的代码:暴力扫描,通过一半测试用例
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
if not strs:
return ""
if strs == [""]:
return ""
i,j = len(strs[0]),len(strs) #i是列 j是行
for k in range(i):
c = strs[0][k]
for l in range(1,j):
if k <= len(strs[l])-1:
if strs[l][k] !=c:
return strs[0][:k]
快速解法:找到字符串数组里的最大最小字符串,找他们的公告前缀就可以
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
maxStr = max(strs)
minStr = min(strs)
for i,x in enumerate(minStr):
if x!=maxStr[i]:
return minStr[:i]
return minStr
力扣162 寻找峰值
自己的解法:暴力,55555,太菜了,只会暴力
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
if len(nums) == 1:
return 0
for i in range(len(nums)):
if i ==0:
if nums[0] > nums[1]:
ans = 0
break
if i == len(nums)-1:
if nums[-1]>nums[-2]:
ans = len(nums)-1
break
if nums[i]>nums[i-1] and nums[i]>nums[i+1]:
ans =i
break
return ans
优秀题解:二分解法
class Solution:
def findPeakElement(self, nums: List[int]) -> int:
left, right = 0, len(nums)-1
while left< right:
mid = (left+right) //2
if nums[mid] > nums[mid+1]:
right = mid
else:
left = mid+1
return left
6.26刷题
力扣40 组合总数||
自己的解法:回溯+去重(超时)
正确解法:回溯+剪枝
剪枝技巧1:设置一个begin,即开始搜索的位置,让begin每次从i+1开始搜索,就不会重复使用自己,保证每个位置被使用一次,还可以保证不会出现123,321这样的情况
剪枝技巧2:还有 1(0) 6(5) 和1(1) 6(5)这种情况,加一个判断i>begin
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
def findPath(nums, begin,target, res, path, size):
if target < 0:
return
if target == 0:
res.append(path)
return
for i in range(begin,size):
if nums[i] == nums[i-1] and i>begin:
continue
findPath(nums,i+1, target - nums[i], res, path + [nums[i]], size)
size = len(candidates)
if size == 0:
return
res = []
candidates.sort()
findPath(candidates,0, target, res, [], size)
return res
力扣17 电话号码的数字组合
本人:暴力枚举
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
number_dict={
'2':['a','b','c'],
'3':['d','e','f'],
'4':['g','h','i'],
'5':['j','k','l'],
'6':['m','n','o'],
'7':['p','q','r','s'],
'8':['t','u','v'],
'9':['w','x','y','z']
}
res = []
size = len(digits)
if size ==1:
return number_dict[digits]
if size == 2:
i, j = digits[0],digits[1]
for v1 in number_dict[i]:
for v2 in number_dict[j]:
res.append(v1+v2)
if size == 3:
i, j, k = digits[0],digits[1],digits[2]
for v1 in number_dict[i]:
for v2 in number_dict[j]:
for v3 in number_dict[k]:
res.append(v1+v2+v3)
if size == 4:
i, j, k, l = digits[0],digits[1],digits[2],digits[3]
for v1 in number_dict[i]:
for v2 in number_dict[j]:
for v3 in number_dict[k]:
for v4 in number_dict[l]:
res.append(v1+v2+v3+v4)
return res
题解:回溯
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits:
return []
number_dict={
'2':['a','b','c'],
'3':['d','e','f'],
'4':['g','h','i'],
'5':['j','k','l'],
'6':['m','n','o'],
'7':['p','q','r','s'],
'8':['t','u','v'],
'9':['w','x','y','z']
}
def letterCom(combination,digits):
if len(digits) == 0:
res.append(combination)
else:
for i in number_dict[digits[0]]:
letterCom(combination+i,digits[1:])
res = []
letterCom("",digits)
return res
6.29
152 乘积最大子数组
跟和最大子数组不一样的是,这里要维护两个变量`
class Solution:
def maxProduct(self, nums: List[int]) -> int:
maxAns = nums[0]
imax = 1
imin = 1
for i in range(len(nums)):
if nums[i]<0:
imax, imin = imin, imax
imax = max(nums[i],imax*nums[i])
imin = min(nums[i],imin*nums[i])
maxAns = max(maxAns,imax)
return maxAns
力扣448 找出消失的数字,原地哈希
class Solution:
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
n = len(nums)
for num in nums:
x = (num - 1) % n #还原数组的下标
nums[x] += n
ret = [i + 1 for i, num in enumerate(nums) if num <= n]
return ret
7.1
力扣 对称二叉树 easy
递归判断左子树的左子树与右子树的右子树 以及 左子树的右子树和右子树的左子树是否相等,有一个为空或者值不等,false,同时为空,true
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if not root:
return True
def dfs(left,right):
# 递归的终止条件是两个节点都为空
# 或者两个节点中有一个为空
# 或者两个节点的值不相等
if not (left or right):
return True
if not (left and right):
return False
if left.val!=right.val:
return False
return dfs(left.left,right.right) and dfs(left.right,right.left)
# 用递归函数,比较左节点,右节点
return dfs(root.left,root.right)
力扣 回文数 easy
解法一:变成字符串再反转
return str(x) == str(x)[::-1]
解法二:反转数字再比较
class Solution:
def isPalindrome(self, x: int) -> bool:
if x<0:
return False
num = x
cur = 0
while(num):
cur = cur*10+num%10
num =num//10
return cur == x
力扣 155 最小栈 简单
class MinStack:
def __init__(self):
self.stack = []
self.min_stack = [math.inf]
def push(self, val: int) -> None:
self.stack.append(val)
self.min_stack.append(min(val,self.min_stack[-1]))
def pop(self) -> None:
self.stack.pop()
self.min_stack.pop()
def top(self) -> int:
return self.stack[-1]
def getMin(self) -> int:
return self.min_stack[-1]
力扣 33 搜索旋转排序数组 中等
解法:二分 注意边界条件的判断
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return -1
left,right = 0,len(nums)-1
while left <= right:
mid = (left+right)//2
if nums[mid] == target:
return mid
#左边有序
if nums[left] <= nums[mid]:
if nums[left] <= target < nums[mid]:
right = mid-1
else:
left = mid+1
else:
if nums[mid] <= target <=nums[right]:
left = mid+1
else:
right = mid-1
return -1
7.2
力扣 48 旋转图像
思路:先水平反转,再沿着对角线反转
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
l1 = len(matrix) #行
for i in range(l1//2):
for j in range(l1):
matrix[i][j], matrix[l1-i-1][j] = matrix[l1-i-1][j], matrix[i][j]
for i in range(l1):
for j in range(0,i):
matrix[i][j] ,matrix[j][i] = matrix[j][i], matrix[i][j]
力扣142 环形链表2 找环的入口
自己:用了一个set
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
if not head or not head.next:
return None
map = set()
p = head
while(p):
if p in map:
return p
map.add(p)
p = p.next
return None
7.3
力扣234 回文链表
我的思路:放到list里反转看是否一致 空间复杂度O(n)
降低空间复杂度的办法:用快慢指针找到中点,从中点以后的链表反转,检查是否一致
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if not head:
return False
def reverlist(head):
pre = None
cur = head
while(cur):
next = cur.next
cur.next = pre
pre = cur
cur = next
return head
slow,fast,preslow = head, head,head
while(fast and fast.next):
pre = slow
slow = slow.next
fast = fast.next.next
pre.next = None
list1 = head
list2 = reverlist(slow)
while(list1):
if list1.val != list2.val:
return False
list1 = list1.next
list2 = list2.next
return True
力扣461 汉明距离
知识点:python & | 表示按位操作
and, or则依据是否非0来决定输出
class Solution:
def hammingDistance(self, x: int, y: int) -> int:
z = x^y
ans = 0
while(z):
ans +=z & 1
z>>=1
return ans
7.4
力扣63 不同路径2
思路:动态规划,处理一下障碍点即可,如果不在第一行第一列,该出dp赋值0,如果在第一行第一列,那么从故障点往后所有值为0
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
m, n = len(obstacleGrid[0]), len(obstacleGrid) #m列数 n行
dp = [[0 for _ in range(m)] for _ in range(n)]
for i in range(m):
if obstacleGrid[0][i] ==1:
break
dp[0][i] = 1
for i in range(n):
if obstacleGrid[i][0] ==1:
break
dp[i][0] = 1
for i in range(1,n):
for j in range(1,m):
if obstacleGrid[i][j] == 1:
continue
else:
dp[i][j] = dp[i-1][j]+dp[i][j-1]
return dp[-1][-1]
64 最小路径和:还是dp,第一行第一列累加即可,其他地方的值为dp[i][j] = min(dp[i-1][j],dp[i][j-1])+nums[i][j]
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
dp = [float(inf)] * (len(grid[0])+1)
dp[1] = 0
for row in grid:
for i ,num in enumerate(row):
dp[i+1] = min(dp[i+1],dp[i])+num
return dp[-1]
7.5
5 最长回文子串 中心扩展法
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
if n ==1:
return s
if n==2:
return s[0] if s[0]!=s[1] else s
res = ''
def find(left, right, res):
while (left >= 0 and right <n and s[left] == s[right]):
left -= 1
right += 1
if right - left - 1 > len(res):
res = s[left+1:right]
return res
for i in range(n):
res=find(i, i + 1, res) #回文子串长度为偶数
res=find(i, i, res)#回文子串长度为奇数
return res
7.6 盛最多水的容器 【中等】
思路:双指针
class Solution:
def maxArea(self, height: List[int]) -> int:
if not height:
return 0
n = len(height)
l, r = 0, n-1
maxans = 0
while l<r:
pre = min(height[l],height[r])*(r-l)
maxans =max(pre, maxans)
if height[l]<height[r]:
l +=1
else:
r -=1
return maxans
78 子集 回溯(以为自己学会了,还是要靠题解,心态崩了)
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
res = []
n = len(nums)
def dfs(nums,path,start):
res.append(path[:])
for i in range(start,n):
path.append(nums[i])
dfs(nums,path,i+1)
path.pop()
dfs(nums,[],0)
return res
7.8 338 比特数计数
小技巧:x&(x-1) 可以消除x的最末一个1
如果x&(x-1) ==0 说明这个数是2的整数幂
class Solution:
def countBits(self, n: int) -> List[int]:
if n==0:
return [0]
high_hit = 0
res = [0]
for i in range(1,n+1):
if i & (i-1) == 0:
high_hit = i
res.append(res[i-high_hit]+1)
return res
647 回文子串 中等
我的解法:中心扩展
class Solution:
def countBits(self, n: int) -> List[int]:
if n==0:
return [0]
high_hit = 0
res = [0]
for i in range(1,n+1):
if i & (i-1) == 0:
high_hit = i
res.append(res[i-high_hit]+1)
return res
剑指03 数组中重复的数字
class Solution:
def findRepeatNumber(self, nums: List[int]) -> int:
'''
用值当下标索引,因为有重复值,就会有冲突
'''
n = len(nums)
for i in range(n):
while i != nums[i]:
if nums[i] == nums[nums[i]]:
return nums[i]
temp = nums[i]
nums[i], nums[temp] = nums[temp], nums[i]
return -1