主要做一些想法的总结
1. 两数之和
哈希表传送门: 哈希表
① 暴力解法
用两层循环,循环获取数据,但会重复读取数据,所以我们需要从第一个和第二个数据开始,并且不要直接读取列表
用下标作为循环的变量,依据下标循环读取列表
在第二个循环使用 第一个循环的下标值+1 作为标准,就能很好的契合我们一组一组比对过去的需求
记得在得到数据后,输出 return i,j 并且 break .若没找到,使用 else: continue 进行再循环
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
n = len(nums)
for i in range(n):
for j in range(i+1,n):
if nums[i]+nums[j] == target:
return i,j
break
else:
continue
② 用 if 判断替代一个 for 循环
第一步一样,也是使用 for 取出下标,再取出数据
第二步 用 target 减去 数值,再用 if ,,
将差值与剩下的数做对比,若一样再用 index() 函数获取该数值的下标,
若下标与第一步的下标一致,则 continue ,因为不能重复.
若不一样,则跳过该次循环
若不一样,跳过该次循环
(D_value :差值 key:下标 ) 耗时是上面的 1/6,内存差不多
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
n = len(nums)
for i in range(n):
D_value=target-nums[i]
if D_value in nums:
key = nums.index(D_value)
if key==i:
continue
else:
return i,key
else:
continue
③ 哈希表
主要是将数组的内容与下标交换,并转化为字典,将 value 作为 key ,index 作为 字典值
利用字典的优势进行优化
先新建一个空的字典, hashmap = {}
使用 enumerate 函数处理 nums 数组,使每个值前带上 index
并从中取出 indx 和 num
用目标值减去 num,并检查是否存在于 hashmap 中
若存在,返回
若不存在,则 赋予 {num: idx} 到 hashmap 中
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hashmap = {}
for idx, num in enumerate(nums):
if target - num in hashmap:
return [hashmap[target - num],idx]
else:
hashmap[num] = idx
2. 把数字翻译成字符串
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
遇到动态规划的题,直接考虑把结果全部算出来是比较难以做到的,一般考虑递推的办法,也就是只考虑所在存在的可能性,根据当前的可能性,不断的循环递归自身,最终求出结果.
那我们只考虑当前的话,那只有两个结果,这个数字和前一个数字可以一起翻译,这个数字和前一个数字不能一起翻译.
假设,当前数字,连续两个数字一起翻译,则剩下的数字的翻译方案数为 f(i-2),单独翻译则剩下的为 f(i-1)
则在当前位置,翻译的方案数为 f(i-2)+f(i-1),即当前翻译一个或者两个,剩下的数字的方案数加起来
若当前位置只能被单独翻译,则方案数与该位置的数字无关,则该位置的方案数等于 i-1 的方案数
设定当前状态:动态规划表 dp,dp[i] 代表以 xi 为结尾的数字的翻译方案数量
若当前可被翻译则,dp[i] = dp[i-1] + dp[i-2],否则 dp[i] = dp[i-1]
再确定一下哪些数字可以被两位数翻译,是 [10,25]
那可不可翻译的条件就可以设定为
与
我们可以从右往左,也可以从左往右,这里使用从右往左,比较契合上面的题解
方法一:字符串遍历
时间复杂度和空间复杂度均为 N
时间复杂度的 N 为字符串 s 的长度,决定了循环次数
空间复杂度为 字符串 s 使用的大小
首先
将数字转化为字符串,str()
设定初始变量 a=b=1
循环次数为 range(len(s)-2,-1,-1)
意思为 s 的长度 减2开始,到 -1 的前一个数字,也就是0.每次步幅为 -1
用例子说明比较形象,比如这里就是
12258
那 i 一开始就是 3,也就是说一开始在识别 i与i+1 是否能组合,,也就是第四位数字和第五位数字
至于为什么要减2 ,主要是一个是位数是从 0 开始的,还有就是长度会比 s 的位数也多一
这里设定公式:
a,b ,a为 f(i) b为f(i-1)
拆开循环算一下,
i = 3, a = 1,s[i:i+2]=58, b = 1
i = 2 ,a = a+b = 2,s[i:i+2] = 25,b = a =1
i = 1,a = a+b=3,s[i:i+2]=22,b=a=2
i=0,a = a+b=5,s[i:i+2]=12,b =a =3
相当于在循环中完成了对 a b 的递归
class Solution:
def translateNum(self, num: int) -> int:
s = str(num)
a = b = 1
for i in range(len(s) - 2, -1, -1):
a, b = (a + b if "10" <= s[i:i + 2] <= "25" else a), a
return a
3. 最佳观光组合
给定正整数数组 A,A[i] 表示第 i 个观光景点的评分,并且两个景点 i 和 j 之间的距离为 j - i。
一对景点(i < j)组成的观光组合的得分为(A[i] + A[j] + i - j):景点的评分之和减去它们两者之间的距离。
返回一对观光景点能取得的最高分
输入:[8,1,5,2,6] 输出:11 解释:i = 0, j = 2,
A[i] + A[j] + i - j = 8 + 5 + 0 - 2 = 11
这个其实也是动态规划,按照前面的思路,动态规划,只考虑当下,才能降低运算的复杂度
看了题目,max()应该是对状态进行改变的一个很好地函数
最先想到的自然是双层 for 循环,一个一个找过去,不过这样明显就是最慢的办法,我们再观察一下分数的计算公式
A[i]+A[j] +i -j ,可以分为两个部分,A[i]+i 为一个部分,A[j]-j 为一个部分,第一个部分有什么特点呢,也就是说 A[i]+i的值是有两部分组成的,一个是位置所在的值,一个是越往后越大.
那我们看一下两层 for 循环的暴力解法
class Solution:
def maxScoreSightseeingPair(self, A: List[int]) -> int:
num_max=A[0]+A[1]-1
for i in range(len(A)):
for j in range(i+1,len(A)):
num_max=max(num_max,A[i]+A[j]+i-j)
return num_max
但我们发现,
比如第一次循环 i 固定为 0,j在 【1,len(A)-1】中变动,
这个时候 A[i] + i 是固定的,而A【j】-j 求的就是后面所有值中的最大值,
那我们如何用这一点来优化我们的函数呢,
也就是说,我们只需要遍历 j ,然后 j 每取一个值,他前面的 i,直接取 i 中的最大值即可,也就是 A【i】+i 的最大值。(当然,你遍历 i ,然后取 A[J]-J 的最大值,好像也行,不过这里采取遍历 i 的办法)
既然我们要遍历 j,那我们就需要从 1 开始
那 max(A[i]+i) 的初始值可以初始化为 A[0]+0
max_value=A[0] + 0
总体的值初始化为 0
ans = 0
对 j 进行循环遍历
for j in range(1,len(A)):
# 这一步就是更新评分值,对比之前的评分,取高评分
ans = max(ans,max_value+A[j]-j)
# 这里虽然写着 A[j]+j 但实际上在题目中代表着,A[i]+i,因为放在了评分后面,所以直接使用 A[j]+j 即可
max_value = max(max_value,A[j]+j)
return ans
class Solution(object):
def maxScoreSightseeingPair(self, A):
"""
:type A: List[int]
:rtype: int
"""
res = 0
pre_max = A[0] + 0 #初始值
for j in range(1, len(A)):
res = max(res, pre_max + A[j] - j) #判断能否刷新res
pre_max = max(pre_max, A[j] + j) #判断能否刷新pre_max, 得到更大的A[i] + i
return res