152. 乘积最大子数组
'''
这个和最大子序和是一样的,只是一个是乘积形式,一个是和的形式
如果想到达i位置,要不经过i-1位置,要不只取i位置
那么就出现了[i]+f(i-1)与i比大小的情况
这里有一个特殊情况:
如果出现了如果f(i-1)=0
那么f(i)=[i]
这里不管[i]是正数还是负数
因为已经有0的记录了,如果后面全是负数,最大值还是可以归到0身上
但是如果为了变成最大而改换成0,那么就会导致后面结果出现误差:
如: [2,3,0,-2,-4]
[2,6,0,0,0] 最大值应该是8,但如果计算了0那么就不是这样了
上面的思路有个问题:
[-2,3,-4]
-4位置的最大值为-2*3*-4=24,那么可以知道
当前位置的最优解未必是由前一个位置的最优解转移得到的。
所以我想了一下,因为负数和正数以及0的存在,导致了可能负负得正
以及正上加正
所以每一次需要记录最小值和最大值
这样就是 最小值*[cur] 最大值*[cur] [cur]
本身里面选择
'''
def fun1(nums):#错误写法,只有正上加正得情况,没有负负得正得情况
max_=nums[0]
pre=nums[0]
for i in range(1,len(nums)):
if pre!=0:
cur=max(pre*nums[i],nums[i])
else:
cur=nums[i]
if max_<cur:
max_=cur
pre=cur
print(max_)
fun1([2,3,0,-2,-4])
def fun2(nums):#代码通过,所谓动态规划就是大量使用已经计算过的结果
max_=nums[0]
pre_max=nums[0]
pre_min=nums[0]
for i in range(1,len(nums)):
cur_max=max(pre_max*nums[i],pre_min*nums[i],nums[i])
cur_min=min(pre_max*nums[i],pre_min*nums[i],nums[i])
if max_<cur_max:
max_=cur_max
pre_max=cur_max
pre_min=cur_min
return max_
print(fun2([]))
===========================================================================================================================================================================
628. 三个数的最大乘积
'''
首先找出三个数,计算他们的乘积
然后找出所有的三个数乘积,这是一种全排列,是可以的方法,但当然不是好的方法
方法一:
排序+max(最后三个数相乘,最后一个数*第一个数*第二个数)
因为存在负数情况,那么负数会导致越小乘积越大
但是不可能是3个负数相乘,所以就取最小的两个负数+最后一个数
'''
def fun1(nums):
nums.sort()
max1=nums[-1]*nums[-2]*nums[-3]
max2=nums[0]*nums[1]*nums[-1]
if max1<max2:
return max1
else:
return max2
'''
方法二:
暴力枚举
暴力思路:
假设有一数组 [1,2,3,4,5]
[0,1,2,3,4]
当前数为1,固定2 然后遍历3,4,5
固定3,然后遍历4,5
固定4,然后遍历5
当前数为2,固定3 然后遍历4,5
固定4 然后遍历5
当前数为3,固定4 然后遍历5
'''
def fun2(nums):#92通过了71个,不能通过很正常
max_=float('-inf')
for i in range(len(nums)):
for j in range(i+1,len(nums)):
cur=j+1
while cur<len(nums):
dangqianshu=nums[i]*nums[j]*nums[cur]
if max_<dangqianshu:
max_=dangqianshu
cur+=1
return max_
'''
方法三:
去他妈的,我以为是动态规划,就是没有动态规划操
线性扫描:
排序的目的是什么?找出最小两个数,和最大三个数
那么很明显,我只需要遍历,然后找到这五个数就好了
'''
===========================================================================================================================================================================
221. 最大正方形
'''
相邻节点:下标与上一层节点下标相同或者上一层节点下标+1
如果在当前行的下标为i,那么下一步可以移动到<<下一行>>的下标i,下标i+1
暴力当然是可以做的,用栈来模拟,记录回来的位置
'''
def fun1(triangle):#答案提交通过
#经典动态规划,本来是没思路的哈哈哈
#结果一准备去看答案,就发现了思路
#动态规划,利用已经出现的计算
dp=[[0]*i for i in range(1,len(triangle)+1)]
min_=float('inf')
dp[0][0]=triangle[0][0]
for i in range(1,len(triangle)):#行遍历
dangqianhang=triangle[i]
for j in range(len(dangqianhang)):#每一次必须走了,不存在跳动的情况
if j>len(dp[i-1])-1:
dp[i][j] = dangqianhang[j] + dp[i - 1][j-1]
elif 0<=j-1:
dp[i][j]=dangqianhang[j]+min(dp[i-1][j],dp[i-1][j-1])
else:
dp[i][j]=dangqianhang[j]+dp[i-1][j]
min_=min(dp[len(triangle)-1])
return min_
def fun2(triangle):
# 上面的代码和答案的思路是一致的,但是答案把代码又优化了一下
len_ = len(triangle)
dp = [[0] * i for i in range(1, len_ + 1)]
dp[0][0] = triangle[0][0]
for i in range(1, len_): # 行遍历
dangqianhang = triangle[i]
# 如果第一次是最左边的元素,那么j==0,也只有这个会导致j-1溢出
dp[i][0] = dangqianhang[0] + dp[i - 1][0]
cd = len(dangqianhang)
for j in range(1, cd - 1): # 每一次必须走了,不存在跳动的情况
dp[i][j] = dangqianhang[j] + min(dp[i - 1][j], dp[i - 1][j - 1])
# 只有最右边的元素,才会导致j>len(dp[i-1])-1
dp[i][cd - 1] = dangqianhang[cd - 1] + dp[i - 1][cd - 2]
min_ = min(dp[len(triangle) - 1])
return min_
'''
空间复杂度可以化简,不过对我来说没必要了
最近都是笔试,面试离我还有一定的距离,就先学会写代码就好
'''
def fun3():
pass
===========================================================================================================================================================================
221. 最大正方形
'''
暴力的方法
怎么暴力?
先行遍历,将所有的将所有的1筛选出来
如果出现了连续的1,可以开始列遍历,看是否可以变成一个正方形
'''
def fun1(matrix):
max_=0#这样的话,在全是0的时候,应该就不需要更替了
for i in range(len(matrix)):#行遍历
hang=matrix[i]
ls=[]
for j in range(len(hang)):
if hang[j]=='1':
ls.append(j)#添加下标
#开始判断是否有下标相邻
qishi=ls[0]
changdu=1
lingshi=[]
for xiabiao in range(1,len(ls)):
if ls[i]-ls[i-1]!=1:
lingshi.append((qishi,changdu))
qishi=i
changdu=1
changdu+=1
lingshi.append((qishi,changdu))
print(lingshi)
def fun2():
#写着写着我突然想到了一个事情
#我为什么不直接按行遍历二维表
#记录所有找到的1的下标
#然后开始循环遍历?
#傻逼,给你的不就是这玩意吗?
pass
def fun3(matrix):#错误代码,主要是在内循环的时候有问题
#不是我开了那么多空间,里面就需要这么多方块
#我觉得上面的暴力方法,实现起来不方便
#那么该怎么办呢?
#遍历行和列,然后cur(当前位置)如果是1那么就开始 相当于开一个表格 开始循环判断是不是都是1
hang=len(matrix)
lie=len(matrix[0])
max_=0
for i in range(hang):
for j in range(lie):
if matrix[i][j]=='1':
max_=max(max_,1)#这是因为起始设置的为0,防止全是0
#开表格
changdu = min(hang-i, lie-j)
tingzhi=False
x=0
y=0
while x<changdu:
while y<changdu:
if matrix[i+x][j+y]!='1':
changdu=max(i+x,i+y)
break
y+=1
x+=1
else:
#不是break结束就可以进来
if max_<changdu:
max_=changdu
print(max_*max_)
def fun4(matrix):
hang = len(matrix)
lie = len(matrix[0])
max_ = 0
for i in range(hang):
for j in range(lie):
if matrix[i][j] == '1':
max_ = max(max_, 1) # 这是因为起始设置的为0,防止全是0
# 开表格
changdu = min(hang - i, lie - j)
for k in range(1, changdu): # 循环长度次数,每一次都是最外围
tingzhi = False
for m in range(k + 1):#也可以分两个循环,每次遍历行和列
if matrix[i + k][j + m] == '0' or matrix[i + m][j + k] == '0':
tingzhi = True
break
if tingzhi:
break
else:
max_ = max(max_, k + 1)
print(max_ * max_)
'''
0 1 1 1 0 0 1 1 1 0
1 1 1 1 0 1 1 2 2 0
0 1 1 1 1 0 1 2 3 1
0 1 1 1 1 0 1 2 3 2
0 0 1 1 1 0 0 1 2 3
dp[i][j]为一个正方形的右下角(这是为了方便dp,如果是左下角,就从右往左,从上往下遍历)
检查矩阵中当前位置matrix[i][j]
如果为0 那么不可能为正方形的右下角,也不可能构成数全为1的矩阵
如果为1 那么那么一定是一个正方形的右下角(最次也是他自己)
问题是这个正方形会有多大呢?
min(dp[i-1][j],dp[i-1][j-1],dp[i][j-1])+1
因为是正方形的原因,所以需要取min
还有问题
对于i==0或者j==0的该这么办?
原封不动,因为不可能组成
'''
def fun5(matrix):#动态规划
lie=len(matrix[0])
hang=len(matrix)
max_=0
dp=[[0]*lie for i in range(hang)]
for j in range(lie):
dp[0][j]=eval(matrix[0][j])
max_=max(max_,dp[0][j])
for j in range(hang):
dp[j][0] = eval(matrix[j][0])
max_=max(max_,dp[j][0])
for i in range(1,hang):
for j in range(1,lie):
if matrix[i][j]=='0':
dp[i][j]==0
else:
dp[i][j]=min(dp[i-1][j],dp[i-1][j-1],dp[i][j-1])+1
max_=max(max_,dp[i][j])
return max_*max_
print(fun5([["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]))
===========================================================================================================================================================================
1277. 统计全为 1 的正方形子矩阵
'''
这个题目本质上和221. 最大正方形是同一类
'''
'''
0 1 1 1 0 0 1 1 1 0
1 1 1 1 0 1 1 2 2 0
0 1 1 1 1 0 1 2 3 1
0 1 1 1 1 0 1 2 3 2
0 0 1 1 1 0 0 1 2 3
动态规划思路:
dp[i][j]是正方形的右下角,这是因为遍历是从左往右,从上往下的
如果当前为matrix[i][j]
如果为0,那么他一定不会是正方形的右下角,也不能参与构建全为1的正方形,所以就记为0
如果为1,那么就这个样子
min(dp[i-1][j-1],dp[i-1][j],dp[i][j-1])+1
注意:
像第一行,第一列,一定与原来相同,可以直接先遍历
'''
def fun1(matrix):
hang = len(matrix)
lie = len(matrix[0])
dp = [[0] * lie for i in range(hang)]
count = 0
for i in range(hang):
dp[i][0] = matrix[i][0]
if dp[i][0] == 1:
count += 1
for j in range(1, lie):
dp[0][j] = matrix[0][j]
if dp[0][j] == 1:
count += 1
for i in range(1, hang):
for j in range(1, lie):
if matrix[i][j] == 0:
dp[i][j] == 0
else:
dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1
count += dp[i][j]
return count
print(fun1([[1,0,1],[1,1,0],[1,1,0]]))
===========================================================================================================================================================================
62. 不同路径
'''
机器人每次都只能向下和向右走
所以第一行,一定都只有一种方法到达
第一列,一定都只有一种方法到达
0 1 2 3 4 5 6
0 [0 0 0 0 0 0 0] 像(1,1)这个位置只能是(1,0)和(0,1)移动到,所以
1 [0 0 0 0 0 0 0]
2 [0 0 0 0 0 0 0]
dp[i][j]=dp[i-1][j]+dp[i][j-1]
'''
def fun1(m,n):
dp = [[1] * n for i in range(m)]
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[m - 1][n - 1]
===========================================================================================================================================================================
63. 不同路径 II
'''
这个题目比不同路径相比多了一个障碍物的设定
这个设定会使得你不是每一次位置,都可以走,但其实也没有难多少(只少我现在感觉)
每次加一个判定,原矩阵[i][j]是否为障碍物
'''
def fun1(obstacleGrid):
hang=len(obstacleGrid)
lie=len(obstacleGrid[0])
dp=[[0]*lie for i in range(hang)]
#这里有一点需要注意,第一行和第一列都是如果堵了一个后面的全被堵完
for j in range(lie):
if obstacleGrid[0][j]==1:
dp[0][j]=0
for i in range(j+1,lie):
dp[0][i]=0
break
else:
dp[0][j]=1
for i in range(hang):
if obstacleGrid[i][0]==1:
dp[i][0]=0
for j in range(i+1,lie):
dp[j][0]=0
break
else:
dp[i][0]=1
for i in range(1,hang):
for j in range(1,lie):
if obstacleGrid[i][j]==1:
dp[i][j]=0
else:
dp[i][j]=dp[i-1][j]+dp[i][j-1]
print(dp)
fun1([[0,0],[1,1],[0,0]])
===========================================================================================================================================================================
64. 最小路径和
'''
只要看到二维数组+最优解问题,一定就是动态规划
动态规划干了什么事,通过开辟空间,记录曾经的结果
并在后续计算中使用
减少了重复计算次数
仔细看了一下题目,....
好吧,好像我这一天做的三个题,都是一个类型的,这样好树立自信心哈哈哈
'''
def fun1(grid):
start=grid[0][0]
hang=len(grid)
lie=len(grid[0])
dp=[[start]*lie for i in range(hang)]
for j in range(1,lie):
dp[0][j]=dp[0][j-1]+grid[0][j]
for i in range(1,hang):
dp[i][0]=dp[i-1][0]+grid[i][0]
for i in range(1,hang):
for j in range(1,lie):
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j]
return dp[hang-1][lie-1]
fun1([[1,3,1],[1,5,1],[4,2,1]])
===========================================================================================================================================================================
119. 杨辉三角 II
'''
这个杨辉三角
每一次的结果,都是左上方和右上方的和(存在只有左上方,只有右上方的情况)
所以很自然的可以想到,动态规划,使用已经得到的结果
'''
def fun1(rowIndex):
dp=[[1]*i for i in range(1,rowIndex+2)]
hang=len(dp)
for i in range(2,hang):#如果行数等于0,1,2不会进来,一会看一下输出结果
lie=len(dp[i])
for j in range(1,lie-1):#不对列的两边进行计算,因为不需要
dp[i][j]=dp[i-1][j]+dp[i-1][j-1]
return dp[rowIndex]
def fun2(rowIndex):
#可以看到,上面在当前行,只会使用到其上一行的结果
#所以可以只保留上一行数组
#但是这样的话,后面就需要加些判断
#奥不对,经过我的思考,发现不需要
pre=[1]
for i in range(rowIndex):
cur=[1]*(len(pre)+1)
for j in range(1,len(cur)-1):#注意第二行是进不来这个循环的
cur[j]=pre[j]+pre[j-1]
pre=cur
return cur
def fun3(rowIndex):
#这个题目还有一个很好的解法
#可以达到,常数级的时间复杂度
#这个放在,CSDN里面了
n=rowIndex
ls=[1]
for m in range(1,n+1):
ls.append(ls[m-1]*(n-m+1)//m)
print(ls)
fun3(3)
===========================================================================================================================================================================
392. 判断子序列
'''
好难受,不想想问题
'''
'''
方法一:
暴力方法,每次进行查找,返回下标
问题:
可能存在多个相同字符,不能只返回第一个下标
'''
def fun1(s,t):
pre=[[] for i in range(len(s))]
if len(s)==1 and len(t)==1:
if s==t:
return True
return False
for i in range(len(s)):
for j in range(len(t)):
if s[i]==t[j]:
pre[i].append(j)
for i in reversed(range(1,len(pre))):
if pre[i]==[] or pre[i-1]==[] or max(pre[i])<=min(pre[i-1]):
return False
return True
print(fun1("aaaaaa","bbaaaa"))
===========================================================================================================================================================================
55. 跳跃游戏
'''
跳跃游戏:
--到达该位置,可能不跳吗?
不可能,因为不跳永远无法到达
'''
'''
暴力肯定是一种方法,所有可能情况,全部列出来就好了
'''
def fun1(nums):
#用栈记录走过的路,不断的进行尝试
stack_xiabiao=[0]#初始下标
stack_item=[0]#初始下标对应元素
while stack_xiabiao!=[]:
if stack_item[-1]<nums[stack_xiabiao[-1]]:
pass
#说明可以继续往后面走
#...
#大致写一下,肯定需要有细节注意
'''
我觉得这种思路更好:
--首先循环遍历,将所有下标可以到达的位置通过二维表进行记录
--如果没有可以到达最后面位置的下标,就直接返回False
--如果有,就再次进行判断,当前下标是否有人可以达到
不断的进行划分,如果可以到达下标0位置则返回True
否则返回False
'''
def fun2(nums):
pass
'''
动态规划
dp[i][j]
i代表当前位置,j代表想要到达位置
dp[i][j]可取值为True或False
每次到达i位置查找dp[i][j]位置是否为True
'''
def fun3(nums):#超出了时间限制
if len(nums) == 1:
return True
dp = [[False] * len(nums) for i in range(len(nums))]
j = 1
while j < len(nums) and j < nums[0] + 1:
dp[0][j] = True
j += 1
for i in range(1, len(nums)):
for chazhao in range(i): # 查看0~i-1位置是否有元素可以达到i位置
if dp[chazhao][i] == True:
# 可以达到i位置进行替换
j = 1
while i + j < len(nums) and j < nums[i] + 1:
dp[i][i + j] = True
j += 1
if dp[i][len(nums) - 1] == True:
return True
if dp[0][len(nums) - 1] == True:
return True
return False
def fun4(nums):
#贪心算法,猜测结果哈哈哈
#维护最大可到达长度
#循环nums列表,直到遍历最后一个item时最大可到达长度<当前元素下标
'''
[2,3,1,1,4]
第一次:下标为0长度为2,最大可到达下标为2
第二次:下标为1长度为3,最大可到达下标为4
第三次:下标为2长度为1,最大可到达下标为3,不进行更改
第四次:下标为3长度为1,最大可到达下标为4,不进行更改
不用看了
第五次:下标为4长度为4,最大可到达下标为4
'''
max_ = 0
for i in range(len(nums)):
if i <= max_: # 这里是需要加个判断的,为什么,因为max_是0~i-1位置的最大可以到达长度,那么说不定i位置是前面到不了的
if i + nums[i] >= max_:
max_ = i + nums[i]
if max_ >= len(nums) - 1:
return True
return False
print(fun4([0,2,3]))
===========================================================================================================================================================================
213. 打家劫舍 II
'''
理解题目:
--围成一圈,即第一个房子和最后一个房子是紧挨着的
--两间相邻房间同一晚上被小偷偷,则会报警
--不触动报警装置,偷取最大金额
这个题目的变化地方在于0和len(nums)-1位置变成了相邻
在打家劫舍中,因为没有围成圈,不需要考虑0和len(nums)-1的问题
所以如果只有一个元素就取他一个就行了
如果有两个元素,[0]的时候取自己,[1]的时候取[0],[1]中的最大值
假设待偷数组为nums
当元素个数>2时
分为两种情况:
当前位置偷:nums[i]+dp[i-2]
当前位置不偷:dp[i-i]
这样可以得到公式
dp[i]=max(dp[i-1],dp[i-2]+nums[i])
每次到达一个位置,都是可以偷的最大值,后面的元素也不会影响前面的结果
所以如果想要[i]位置最大,一定是在这两种情况里面选
'''
#这个写的是打家劫舍的代码
def fun1(nums):#执行通过
if len(nums)<=2:
return max(nums)
dp = [0] * len(nums)
dp[0]=nums[0]
dp[1]=max(nums[0],nums[1])
for i in range(2,len(nums)):
dp[i]=max(dp[i-1],dp[i-2]+nums[i])
return dp[len(nums)-1]
def fun2(nums):#把时间复杂度化简一下
#我实际用到的就两个变量[i-2]和[i-1]
if len(nums)<=2:
return max(nums)
pre1=nums[0]#[i-2]
pre2=max(nums[0],nums[1])#[i-1]
for i in range(2,len(nums)):
t=pre2
pre2=max(pre2,pre1+nums[i])
pre1=t
return pre2
#打家劫舍2的代码
'''
基本思路:
--与打家劫舍相比,0和len(nums)-1相邻
--进行拆分变成从0到len(nums)-2和len(nums)-1到1
--为什么?
因为有我没他
'''
def fun3(nums):
if len(nums)<=3:
return max(nums)
pre1=nums[0]
pre2=max(nums[0],nums[1])
for i in range(2,len(nums)-1):
t=pre2#更换
pre2=max(pre2,pre1+nums[i])#获得当前位置可以达到的最大值
pre1=t
max1=pre2
pre1 = nums[len(nums)-1]
pre2 = max(nums[len(nums)-1], nums[len(nums)-2])
for i in reversed(range(1,len(nums)-2)):
t = pre2 # 更换
pre2 = max(pre2, pre1 + nums[i]) # 获得当前位置可以达到的最大值
pre1 = t
return max(max1,pre2)
fun3([2,3,3,3,3,3,9])
===========================================================================================================================================================================
在这里插入代码片
===========================================================================================================================================================================
在这里插入代码片