文章目录
前言
整理力扣刷题思路。
- 语言:python
- 题库:来自neetcode: link
一、预备知识
贪心算法
贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。贪心算法并不保证会得到最优解,但在某些问题中,贪心却能得到最优解。
贪心算法的特点:
- 局部最优选择:在每个阶段,算法会做出一个看似最优的决定,而不考虑更大的问题。
- 无后效性:一旦作出这些决策,就不再重新考虑。
- 问题分解:如果一个问题可以通过贪心法解决,通常意味着整个问题的最优解可以通过组合局部最优解得到。
举个简单的例子,如在找零问题中,假设你是售货员,需要给客户找零n元,而钱箱中有无限个1元、5元、10元、20元、50元、100元人民币。你希望让客户拿到的纸币数量尽可能少。在这个问题中,一个有效的贪心策略是从最大的纸币开始,尽可能多地使用,然后依次减小纸币面额,直到找完所有的零钱。
二、解题思路
1.贪心算法
53.maximum-subarray
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组是数组中的一个连续部分。
link
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
#直接在原数组上修改
for i in range(1,len(nums)):
nums[i] += max(nums[i-1],0)
return max(nums)
55.jump-game
给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。
link
class Solution:
def canJump(self, nums: List[int]) -> bool:
'''
贪心算法:维护可到达的最远位置mx,只要能到达mx,那一定也能到达mx之前的所有位置
'''
if len(nums) == 1:
return True
mx = nums[0]
for i in range(1,len(nums)):
#如果最远也到达不了当前位置,则更加到不了最后的位置
if mx<i:
return False
else:
#更新最远可到达的位置
mx = max(mx,i+nums[i])
#如果已经可以到达最后位置,提前返回True结束遍历
if mx>=len(nums)-1:
return True
45.jump-game-ii
给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。
每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:
- 0 <= j <= nums[i]
- i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。
link
class Solution:
def jump(self, nums: List[int]) -> int:
if len(nums)==1:
return 0
#维护步数、当前位置及之前可到达的最远位置、当前步数下可到达的最远位置
step,mx,right = 0,0,0
for i in range(len(nums)-1):
mx = max(mx,i+nums[i])
#若已到达当前步数下可到的最远端,则步数加1,更新最远位置
if i==right:
step += 1
right = mx
return step
class Solution:
def jump(self, nums: List[int]) -> int:
#[l,r]是当前步数所能到达的位置区间
l=r=step=0
while r<len(nums)-1:
farest = 0
for i in range(l,r+1):
farest = max(farest,i+nums[i])
l = r+1
r = farest
step += 1
return step
134.gas-station
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
link
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
'''
本题的逻辑是:
1.如果gas总量小于cost总量,一定无法环一圈
2.如果gas总量大于或等于cost总量,一定可以环一圈
'''
if sum(gas)<sum(cost):
return -1
start = 0
rest = 0
for i in range(len(gas)):
rest += gas[i]-cost[i]
if rest<0:
start = i+1
rest = 0
return start
763.partition-labels
给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。
返回一个表示每个字符串片段的长度的列表。
link
class Solution:
def partitionLabels(self, s: str) -> List[int]:
dict = {}
#字典只存储每个字符最后出现的位置
for i in range(len(s)):
dict[s[i]] = i
cnt = 0
boundary = dict[s[0]]
ans = []
for i in range(len(s)):
cnt += 1
if dict[s[i]]>boundary:
boundary = dict[s[i]]
if i==boundary:
ans.append(cnt)
cnt = 0
return ans
678.valid-parenthesis-string
给你一个只包含三种字符的字符串,支持的字符类型分别是 ‘(’、‘)’ 和 ‘*’。请你检验这个字符串是否为有效字符串,如果是有效字符串返回 true 。
有效字符串符合如下规则:
任何左括号 ‘(’ 必须有相应的右括号 ‘)’。
任何右括号 ‘)’ 必须有相应的左括号 ‘(’ 。
左括号 ‘(’ 必须在对应的右括号之前 ‘)’。
‘*’ 可以被视为单个右括号 ‘)’ ,或单个左括号 ‘(’ ,或一个空字符串。
一个空字符串也被视为有效字符串。
link
class Solution:
def checkValidString(self, s: str) -> bool:
'''
总共可分为三种情况:(=),(>),(<)
'''
cnt1=cnt2=0
#正序遍历,将*看成(,确保有足够的(
for i in s:
cnt1 += -1 if i==')' else 1
if cnt1<0:
return False
#逆序遍历,将*看成),确保有足够的)
for i in reversed(s):
cnt2 += -1 if i=='(' else 1
if cnt2<0:
return False
return True
846.hand-of-straights
Alice 手中有一把牌,她想要重新排列这些牌,分成若干组,使每一组的牌数都是 groupSize ,并且由 groupSize 张连续的牌组成。
给你一个整数数组 hand 其中 hand[i] 是写在第 i 张牌上的数值。如果她可能重新排列这些牌,返回 true ;否则,返回 false 。
link
class Solution:
def isNStraightHand(self, hand: List[int], groupSize: int) -> bool:
if len(hand)%groupSize != 0:
return False
dic = {}
for i in hand:
dic[i] = 1+dic.get(i,0)
#每次取最小牌,去掉对应的顺子
for key in sorted(dic.keys()):
if dic[key]>0:
tmp = dic[key]
for i in range(key,key+groupSize):
if i not in dic or dic[i]<tmp:
return False
dic[i] -= tmp
return True
1899.merge-triplets-to-form-target-triplet
三元组 是一个由三个整数组成的数组。给你一个二维整数数组 triplets ,其中 triplets[i] = [ai, bi, ci] 表示第 i 个 三元组 。同时,给你一个整数数组 target = [x, y, z] ,表示你想要得到的 三元组 。
为了得到 target ,你需要对 triplets 执行下面的操作 任意次(可能 零 次):
选出两个下标(下标 从 0 开始 计数)i 和 j(i != j),并 更新 triplets[j] 为 [max(ai, aj), max(bi, bj), max(ci, cj)] 。
例如,triplets[i] = [2, 5, 3] 且 triplets[j] = [1, 7, 5],triplets[j] 将会更新为 [max(2, 1), max(5, 7), max(3, 5)] = [2, 7, 5] 。
如果通过以上操作我们可以使得目标 三元组 target 成为 triplets 的一个 元素 ,则返回 true ;否则,返回 false 。
link
class Solution:
def mergeTriplets(self, triplets: List[List[int]], target: List[int]) -> bool:
a=b=c=False
for i,j,k in triplets:
if i>target[0] or j>target[1] or k>target[2]:
continue
if i==target[0]:
a = True
if j==target[1]:
b = True
if k==target[2]:
c = True
return a and b and c