Merge pull request #2116 from jianghongcheng/master

更新 贪心。。。。
This commit is contained in:
程序员Carl
2023-06-01 16:29:48 +08:00
committed by GitHub
10 changed files with 382 additions and 197 deletions

View File

@ -205,66 +205,81 @@ class Solution {
```
### Python
贪心(版本一)
```python
class Solution:
def jump(self, nums: List[int]) -> int:
if len(nums) == 1: return 0
ans = 0
curDistance = 0
nextDistance = 0
for i in range(len(nums)):
nextDistance = max(i + nums[i], nextDistance)
if i == curDistance:
if curDistance != len(nums) - 1:
ans += 1
curDistance = nextDistance
if nextDistance >= len(nums) - 1: break
return ans
```
```python
# 贪心版本二
class Solution:
def jump(self, nums: List[int]) -> int:
def jump(self, nums):
if len(nums) == 1:
return 0
curDistance, nextDistance = 0, 0
step = 0
for i in range(len(nums)-1):
nextDistance = max(nextDistance, nums[i]+i)
if i == curDistance:
curDistance = nextDistance
step += 1
return step
```
```python
# 贪心版本三 - 类似55-跳跃游戏’写法
class Solution:
def jump(self, nums) -> int:
if len(nums)==1: return 0
i = 0
count = 0
cover = 0
while i<=cover:
for i in range(i,cover+1):
cover = max(nums[i]+i,cover)
if cover>=len(nums)-1: return count+1
count+=1
cur_distance = 0 # 当前覆盖最远距离下标
ans = 0 # 记录走的最大步数
next_distance = 0 # 下一步覆盖最远距离下标
for i in range(len(nums)):
next_distance = max(nums[i] + i, next_distance) # 更新下一步覆盖最远距离下标
if i == cur_distance: # 遇到当前覆盖最远距离下标
ans += 1 # 需要走下一步
cur_distance = next_distance # 更新当前覆盖最远距离下标(相当于加油了)
if next_distance >= len(nums) - 1: # 当前覆盖最远距离达到数组末尾不用再做ans++操作,直接结束
break
return ans
```
贪心(版本二)
```python
# 动态规划做法
class Solution:
def jump(self, nums):
cur_distance = 0 # 当前覆盖的最远距离下标
ans = 0 # 记录走的最大步数
next_distance = 0 # 下一步覆盖的最远距离下标
for i in range(len(nums) - 1): # 注意这里是小于len(nums) - 1这是关键所在
next_distance = max(nums[i] + i, next_distance) # 更新下一步覆盖的最远距离下标
if i == cur_distance: # 遇到当前覆盖的最远距离下标
cur_distance = next_distance # 更新当前覆盖的最远距离下标
ans += 1
return ans
```
贪心(版本三) 类似55-跳跃游戏’写法
```python
class Solution:
def jump(self, nums) -> int:
if len(nums)==1: # 如果数组只有一个元素不需要跳跃步数为0
return 0
i = 0 # 当前位置
count = 0 # 步数计数器
cover = 0 # 当前能够覆盖的最远距离
while i <= cover: # 当前位置小于等于当前能够覆盖的最远距离时循环
for i in range(i, cover+1): # 遍历从当前位置到当前能够覆盖的最远距离之间的所有位置
cover = max(nums[i]+i, cover) # 更新当前能够覆盖的最远距离
if cover >= len(nums)-1: # 如果当前能够覆盖的最远距离达到或超过数组的最后一个位置,直接返回步数+1
return count+1
count += 1 # 每一轮遍历结束后,步数+1
```
动态规划
```python
class Solution:
def jump(self, nums: List[int]) -> int:
result = [10**4+1]*len(nums)
result[0]=0
for i in range(len(nums)):
for j in range(nums[i]+1):
if i+j<len(nums): result[i+j]=min(result[i+j],result[i]+1)
#print(result) #打印数组
return result[-1]
result = [10**4+1] * len(nums) # 初始化结果数组,初始值为一个较大的数
result[0] = 0 # 起始位置的步数为0
for i in range(len(nums)): # 遍历数组
for j in range(nums[i] + 1): # 在当前位置能够跳跃的范围内遍历
if i + j < len(nums): # 确保下一跳的位置不超过数组范围
result[i + j] = min(result[i + j], result[i] + 1) # 更新到达下一跳位置的最小步数
return result[-1] # 返回到达最后一个位置的最小步数
```

View File

@ -198,21 +198,35 @@ class Solution {
```
### Python
暴力法
```python
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
result = -float('inf')
def maxSubArray(self, nums):
result = float('-inf') # 初始化结果为负无穷大
count = 0
for i in range(len(nums)): # 设置起始位置
count = 0
for j in range(i, len(nums)): # 从起始位置i开始遍历寻找最大值
count += nums[j]
result = max(count, result) # 更新最大值
return result
```
```python
class Solution:
def maxSubArray(self, nums):
result = float('-inf') # 初始化结果为负无穷大
count = 0
for i in range(len(nums)):
count += nums[i]
if count > result:
if count > result: # 取区间累计的最大值(相当于不断确定最大子序终止位置)
result = count
if count <= 0:
if count <= 0: # 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和
count = 0
return result
```
```
### Go
```go

View File

@ -140,18 +140,24 @@ class Solution {
### Python
```python
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
if len(intervals) == 0: return intervals
intervals.sort(key=lambda x: x[0])
def merge(self, intervals):
result = []
result.append(intervals[0])
if len(intervals) == 0:
return result # 区间集合为空直接返回
intervals.sort(key=lambda x: x[0]) # 按照区间的左边界进行排序
result.append(intervals[0]) # 第一个区间可以直接放入结果集中
for i in range(1, len(intervals)):
last = result[-1]
if last[1] >= intervals[i][0]:
result[-1] = [last[0], max(last[1], intervals[i][1])]
if result[-1][1] >= intervals[i][0]: # 发现重叠区间
# 合并区间,只需要更新结果集最后一个区间的右边界,因为根据排序,左边界已经是最小的
result[-1][1] = max(result[-1][1], intervals[i][1])
else:
result.append(intervals[i])
result.append(intervals[i]) # 区间不重叠
return result
```
### Go

View File

@ -249,44 +249,74 @@ class Solution {
```
### Python
暴力法
```python
# 解法1
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
n = len(gas)
cur_sum = 0
min_sum = float('inf')
for i in range(n):
cur_sum += gas[i] - cost[i]
min_sum = min(min_sum, cur_sum)
if cur_sum < 0: return -1
if min_sum >= 0: return 0
for j in range(n - 1, 0, -1):
min_sum += gas[j] - cost[j]
if min_sum >= 0:
return j
return -1
```
```python
# 解法2
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
start = 0
curSum = 0
totalSum = 0
for i in range(len(cost)):
rest = gas[i] - cost[i] # 记录剩余油量
index = (i + 1) % len(cost) # 下一个加油站的索引
while rest > 0 and index != i: # 模拟以i为起点行驶一圈如果有rest==0那么答案就不唯一了
rest += gas[index] - cost[index] # 更新剩余油量
index = (index + 1) % len(cost) # 更新下一个加油站的索引
if rest >= 0 and index == i: # 如果以i为起点跑一圈剩余油量>=0并且回到起始位置
return i # 返回起始位置i
return -1 # 所有起始位置都无法环绕一圈,返回-1
```
贪心版本一
```python
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
curSum = 0 # 当前累计的剩余油量
minFuel = float('inf') # 从起点出发,油箱里的油量最小值
for i in range(len(gas)):
rest = gas[i] - cost[i]
curSum += rest
if curSum < minFuel:
minFuel = curSum
if curSum < 0:
return -1 # 情况1整个行程的总消耗大于总供给无法完成一圈
if minFuel >= 0:
return 0 # 情况2从起点出发到任何一个加油站时油箱的剩余油量都不会小于0可以从起点出发完成一圈
for i in range(len(gas) - 1, -1, -1):
rest = gas[i] - cost[i]
minFuel += rest
if minFuel >= 0:
return i # 情况3找到一个位置使得从该位置出发油箱的剩余油量不会小于0返回该位置的索引
return -1 # 无法完成一圈
```
贪心版本二
```python
class Solution:
def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
curSum = 0 # 当前累计的剩余油量
totalSum = 0 # 总剩余油量
start = 0 # 起始位置
for i in range(len(gas)):
curSum += gas[i] - cost[i]
totalSum += gas[i] - cost[i]
if curSum < 0:
curSum = 0
start = i + 1
if totalSum < 0: return -1
if curSum < 0: # 当前累计剩余油量curSum小于0
start = i + 1 # 起始位置更新为i+1
curSum = 0 # curSum重新从0开始累计
if totalSum < 0:
return -1 # 总剩余油量totalSum小于0说明无法环绕一圈
return start
```
### Go

View File

@ -178,13 +178,21 @@ class Solution {
class Solution:
def candy(self, ratings: List[int]) -> int:
candyVec = [1] * len(ratings)
# 从前向后遍历,处理右侧比左侧评分高的情况
for i in range(1, len(ratings)):
if ratings[i] > ratings[i - 1]:
candyVec[i] = candyVec[i - 1] + 1
for j in range(len(ratings) - 2, -1, -1):
if ratings[j] > ratings[j + 1]:
candyVec[j] = max(candyVec[j], candyVec[j + 1] + 1)
return sum(candyVec)
# 从后向前遍历,处理左侧比右侧评分高的情况
for i in range(len(ratings) - 2, -1, -1):
if ratings[i] > ratings[i + 1]:
candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1)
# 统计结果
result = sum(candyVec)
return result
```
### Go

View File

@ -248,20 +248,45 @@ class Solution {
```
### Python
贪心 基于左边界
```python
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
if len(intervals) == 0: return 0
intervals.sort(key=lambda x: x[1])
count = 1 # 记录非交叉区间的个数
end = intervals[0][1] # 记录区间分割点
if not intervals:
return 0
intervals.sort(key=lambda x: x[0]) # 按照左边界升序排序
count = 0 # 记录重叠区间数量
for i in range(1, len(intervals)):
if end <= intervals[i][0]:
if intervals[i][0] < intervals[i - 1][1]: # 存在重叠区间
intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]) # 更新重叠区间的右边界
count += 1
end = intervals[i][1]
return len(intervals) - count
```
return count
```
贪心 基于左边界 把452.用最少数量的箭引爆气球代码稍做修改
```python
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
if not intervals:
return 0
intervals.sort(key=lambda x: x[0]) # 按照左边界升序排序
result = 1 # 不重叠区间数量初始化为1因为至少有一个不重叠的区间
for i in range(1, len(intervals)):
if intervals[i][0] >= intervals[i - 1][1]: # 没有重叠
result += 1
else: # 重叠情况
intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]) # 更新重叠区间的右边界
return len(intervals) - result
```
### Go
```go
func eraseOverlapIntervals(intervals [][]int) int {

View File

@ -164,17 +164,110 @@ class Solution {
### Python
暴力
```python
class Solution:
def monotoneIncreasingDigits(self, n: int) -> int:
a = list(str(n))
for i in range(len(a)-1,0,-1):
if int(a[i]) < int(a[i-1]):
a[i-1] = str(int(a[i-1]) - 1)
a[i:] = '9' * (len(a) - i) #python不需要设置flag值直接按长度给9就好了
return int("".join(a))
```
def checkNum(self, num):
max_digit = 10
while num:
digit = num % 10
if max_digit >= digit:
max_digit = digit
else:
return False
num //= 10
return True
def monotoneIncreasingDigits(self, N):
for i in range(N, 0, -1):
if self.checkNum(i):
return i
return 0
```
贪心(版本一)
```python
class Solution:
def monotoneIncreasingDigits(self, N: int) -> int:
# 将整数转换为字符串
strNum = str(N)
# flag用来标记赋值9从哪里开始
# 设置为字符串长度为了防止第二个for循环在flag没有被赋值的情况下执行
flag = len(strNum)
# 从右往左遍历字符串
for i in range(len(strNum) - 1, 0, -1):
# 如果当前字符比前一个字符小,说明需要修改前一个字符
if strNum[i - 1] > strNum[i]:
flag = i # 更新flag的值记录需要修改的位置
# 将前一个字符减1以保证递增性质
strNum = strNum[:i - 1] + str(int(strNum[i - 1]) - 1) + strNum[i:]
# 将flag位置及之后的字符都修改为9以保证最大的递增数字
for i in range(flag, len(strNum)):
strNum = strNum[:i] + '9' + strNum[i + 1:]
# 将最终的字符串转换回整数并返回
return int(strNum)
```
贪心(版本二)
```python
class Solution:
def monotoneIncreasingDigits(self, N: int) -> int:
# 将整数转换为字符串
strNum = list(str(N))
# 从右往左遍历字符串
for i in range(len(strNum) - 1, 0, -1):
# 如果当前字符比前一个字符小,说明需要修改前一个字符
if strNum[i - 1] > strNum[i]:
strNum[i - 1] = str(int(strNum[i - 1]) - 1) # 将前一个字符减1
# 将修改位置后面的字符都设置为9因为修改前一个字符可能破坏了递增性质
for j in range(i, len(strNum)):
strNum[j] = '9'
# 将列表转换为字符串,并将字符串转换为整数并返回
return int(''.join(strNum))
```
贪心(版本三)
```python
class Solution:
def monotoneIncreasingDigits(self, N: int) -> int:
# 将整数转换为字符串
strNum = list(str(N))
# 从右往左遍历字符串
for i in range(len(strNum) - 1, 0, -1):
# 如果当前字符比前一个字符小,说明需要修改前一个字符
if strNum[i - 1] > strNum[i]:
strNum[i - 1] = str(int(strNum[i - 1]) - 1) # 将前一个字符减1
# 将修改位置后面的字符都设置为9因为修改前一个字符可能破坏了递增性质
strNum[i:] = '9' * (len(strNum) - i)
# 将列表转换为字符串,并将字符串转换为整数并返回
return int(''.join(strNum))
```
贪心(版本四)精简
```python
class Solution:
def monotoneIncreasingDigits(self, N: int) -> int:
strNum = str(N)
for i in range(len(strNum) - 1, 0, -1):
# 如果当前字符比前一个字符小,说明需要修改前一个字符
if strNum[i - 1] > strNum[i]:
# 将前一个字符减1以保证递增性质
# 使用字符串切片操作将修改后的前面部分与后面部分进行拼接
strNum = strNum[:i - 1] + str(int(strNum[i - 1]) - 1) + '9' * (len(strNum) - i)
return int(strNum)
```
### Go
```go
func monotoneIncreasingDigits(N int) int {

View File

@ -231,83 +231,56 @@ class Solution{
```
### Python
贪心(版本一)
```python
class Solution:
def partitionLabels(self, s: str) -> List[int]:
hash = [0] * 26
for i in range(len(s)):
hash[ord(s[i]) - ord('a')] = i
last_occurrence = {} # 存储每个字符最后出现的位置
for i, ch in enumerate(s):
last_occurrence[ch] = i
result = []
left = 0
right = 0
for i in range(len(s)):
right = max(right, hash[ord(s[i]) - ord('a')])
if i == right:
result.append(right - left + 1)
left = i + 1
start = 0
end = 0
for i, ch in enumerate(s):
end = max(end, last_occurrence[ch]) # 找到当前字符出现的最远位置
if i == end: # 如果当前位置是最远位置,表示可以分割出一个区间
result.append(end - start + 1)
start = i + 1
return result
# 解法二(不相交区间法)
```
贪心版本二与452.用最少数量的箭引爆气球 (opens new window)、435.无重叠区间 (opens new window)相同的思路。
```python
class Solution:
def partitionLabels(self, s: str) -> List[int]:
# 记录每个字母出现的区间
def getBord(s):
hash = [[-float('inf')] * 2 for _ in range(26)]
for i in range(len(s)):
if hash[ord(s[i]) - ord('a')][0] == -float('inf'):
hash[ord(s[i]) - ord('a')][0] = i
hash[ord(s[i]) - ord('a')][1] = i
# 去除字符串中未出现的字母所占用区间
hash_filter = []
for item in hash:
if item[0] != -float('inf'): hash_filter.append(item)
return hash_filter
# 得到无重叠区间题意中的输入样例格式:区间列表
hash = getBord(s)
# 按照左边界从小到大排序
hash.sort(key= lambda x: x[0])
res = []
left = 0
# 记录最大右边界
right = hash[0][1]
def countLabels(self, s):
# 初始化一个长度为26的区间列表初始值为负无穷
hash = [[float('-inf'), float('-inf')] for _ in range(26)]
hash_filter = []
for i in range(len(s)):
if hash[ord(s[i]) - ord('a')][0] == float('-inf'):
hash[ord(s[i]) - ord('a')][0] = i
hash[ord(s[i]) - ord('a')][1] = i
for i in range(len(hash)):
# 一旦下一区间左边界大于当前右边界,即可认为出现分割点
if hash[i][0] > right:
res.append(right - left + 1)
left = hash[i][0]
# 实时更新最大右边界
right = max(right, hash[i][1])
# 最右侧区间字符串长度为1时的特殊情况也包含于其中
res.append(right - left + 1)
if hash[i][0] != float('-inf'):
hash_filter.append(hash[i])
return hash_filter
def partitionLabels(self, s):
res = []
hash = self.countLabels(s)
hash.sort(key=lambda x: x[0]) # 按左边界从小到大排序
rightBoard = hash[0][1] # 记录最大右边界
leftBoard = 0
for i in range(1, len(hash)):
if hash[i][0] > rightBoard: # 出现分割点
res.append(rightBoard - leftBoard + 1)
leftBoard = hash[i][0]
rightBoard = max(rightBoard, hash[i][1])
res.append(rightBoard - leftBoard + 1) # 最右端
return res
# 解法三:区间合并法 (结合下一题 56. Merge Intervals 的写法)
class Solution: #
def partitionLabels(self, s: str) -> List[int]:
aaa = list(set(s))
#aaa.sort()
bbb = list(s)
ccc = []
for i in reversed(bbb):
ccc.append(i)
intervals = []
for i in range(len(aaa)):
intervals.append([bbb.index(aaa[i]),len(bbb)-ccc.index(aaa[i])-1])
# 先求出各个字母的存在区间,之后利用区间合并方法得出所有不相邻的最大区间。
intervals.sort(key = lambda x:x[0])
newinterval = []
left, right = intervals[0][0], intervals[0][1]
for i in range(1,len(intervals)):
if intervals[i][0] in range(left, right+1):
right = max(intervals[i][1],intervals[i-1][1],right)
left = min(intervals[i-1][0],left)
else:
newinterval.append(right-left+1)
left = intervals[i][0]
right = intervals[i][1]
newinterval.append(right-left+1)
return newinterval
```
### Go

View File

@ -164,24 +164,39 @@ class Solution {
```python
class Solution:
def lemonadeChange(self, bills: List[int]) -> bool:
five, ten = 0, 0
five = 0
ten = 0
twenty = 0
for bill in bills:
# 情况一收到5美元
if bill == 5:
five += 1
elif bill == 10:
if five < 1: return False
five -= 1
# 情况二收到10美元
if bill == 10:
if five <= 0:
return False
ten += 1
else:
if ten > 0 and five > 0:
ten -= 1
five -= 1
# 情况三收到20美元
if bill == 20:
# 先尝试使用10美元和5美元找零
if five > 0 and ten > 0:
five -= 1
elif five > 2:
ten -= 1
#twenty += 1
# 如果无法使用10美元找零则尝试使用三张5美元找零
elif five >= 3:
five -= 3
#twenty += 1
else:
return False
return True
```
### Go

View File

@ -131,17 +131,23 @@ class Solution {
```
### Python
贪心
```python
class Solution:
def largestSumAfterKNegations(self, A: List[int], K: int) -> int:
A = sorted(A, key=abs, reverse=True) # 将A按绝对值从大到小排列
for i in range(len(A)):
if K > 0 and A[i] < 0:
A.sort(key=lambda x: abs(x), reverse=True) # 第一步按照绝对值降序排序数组A
for i in range(len(A)): # 第二步执行K次取反操作
if A[i] < 0 and K > 0:
A[i] *= -1
K -= 1
if K > 0:
A[-1] *= (-1)**K #取A最后一个数只需要写-1
return sum(A)
if K % 2 == 1: # 第三步如果K还有剩余次数将绝对值最小的元素取反
A[-1] *= -1
result = sum(A) # 第四步计算数组A的元素和
return result
```
### Go