python股票交易最大值最多可以完成k比交易_leetcode_贪心算法_python

leetcode455.分发饼干

题目:假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

思路:将饼干大小和胃口大小分别排序后,遍历饼干,尽可能用最小的饼干满足孩子。易错点:如果用饼干进行遍历,注意判断孩子的索引是否会出界。

leetcode376.摆动序列

题目:如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。

思路:首先要单独处理空列表的情况,因为除了空列表外其他情况下至少有一个元素可以作为摆动序列(默认第一个元素可以列入序列,所以下面的考量都从第二个开始)。

方法一:利用一个特点:局部极大值和局部极小值的差最多为1.易错点:严格大或严格小时更新。

classSolution:def wiggleMaxLength(self, nums: List[int]) ->int:if len(nums) < 2:returnlen(nums)

num_up= 1num_down= 1

for i in range(1,len(nums)):#判断i是什么

if nums[i] - nums[i-1] >0:

num_up= num_down+1

elif nums[i] - nums[i-1]<0:

num_down= num_up+1

方法二:记录上一个峰值的属性,并且与现在的对比。易错点:注意pre_diff记录的是上一个峰值的属性,需要在if里更新。否则当遇到diff = 0时只要下一个非0就会更结果,但可能是连续上升的中间有一段平,非局部极值。

classSolution:def wiggleMaxLength(self, nums: List[int]) ->int:if notnums:return0

res= 1pre_diff=0for i in range(1, len(nums)):

cur_diff= nums[i] - nums[i-1]if pre_diff>=0 and cur_diff < 0 or pre_diff <= 0 and cur_diff >0:

res+= 1pre_diff= cur_diff#注意在if里更新 非外

return res

leetcode53.最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

思路:类似问题关键点,更新最大值,计算当前和分开处理。这样做的好处时,计算当前和时可以直接计算,不需要考虑是否需要存下来,当前和只是一个参考值,结果在于更新最大值上。先计算本轮的和,更新最大值,如果本轮和小于0,则会“拖累”后面,所以清零当前和。注意,先更新最大值,再清零。

classSolution:def maxSubArray(self, nums: List[int]) ->int:

max_val= float('-inf')

sum_val=0for i in range(len(nums)):#一定要注意顺序 先算这一轮的和 再更新max,最后决定这一轮是否清0

sum_val = sum_val+nums[i]

max_val=max(max_val,sum_val)if sum_val<0:

sum_val=0return max_val

leetcode122.买卖股票的最佳时机2

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

思路:profit = price[i] - price[j] = price[i]-price[i-1]+price[i-1]-price[i-2]+...-price[j]则相当于每天的利润相加,当该日利润大于0则应当卖掉。所以应当计算单日利润,大于0的即为应当操作的地方。

leetcode55.跳跃游戏

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标。

思路:在每个点更新最远能到达的位置,如果最后最远大于等于最后一个下标,则可以到达。注意在每轮循环中首先确定该位置i是否能到达。

leetcode45.跳跃游戏2

给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。

思路:记录当前的步数,和当前步数下最多可以到达的小标位置。记录比当前步数多一步可以到达的下标位置。每到一步首先判断该位置是否可以在当前步数下到达,如果可以则步数不变,不可以则步数增一并更新当前步数可以到达的位置。然后更新比当前步数多一步可以到达的位置。注意先判断当前位置是否可达,再更新比当前位置多一步可达的位置。如果当前位置不可达而更新的多一步可达的位置,则该位置实际上是多两步可达的位置(首先多一步到达该位置,然后多一步到达该位置可达的位置)。

classSolution:def jump(self, nums: List[int]) ->int:

steps=0

max_reach_currrent_steps=0

max_reach_onemore_steps=nums[0]for i in range(1, len(nums)):if i >max_reach_currrent_steps:

steps+= 1max_reach_currrent_steps=max_reach_onemore_steps

max_reach_onemore_steps= max(i+nums[i], max_reach_onemore_steps)return steps

leetcode1005.K次取反后最大化数组的和

给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。)以这种方式修改数组后,返回数组可能的最大和。

思路:方法一:从小到大升序排列,从左到右如果是负数则取反直至遍历完成或K用完。若K没用完,则返回总和减去两倍的最小值。

方法二:只用排一次顺序。按数字的绝对值降序排列,在K没有用完的情况下优先对绝对值大的负数取反。最后如果K没用完且剩余为奇数,则对最后一个(即绝对值最小的取反)再算总和。

leetcode134.加油站

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明: 如果题目有解,该答案即为唯一答案。输入数组均为非空数组,且长度相同。输入数组中的元素均为非负数。

思路:首先定义一个车在某站的油量cur不包括该站加的油。无所谓从哪里开始,因为相当于一个曲线上以不同的点为默认“0”,遍历了一下曲线,无论最小值的大小是多少,取到该值的点都是一样的。所以默认开始时的油量为0,路上记录油量的最小值和产生最小值的索引。注意:cur+gas[i]-cost[i]实际上是到达i+1个点的剩余油量,所以应当记为i+1。当站的索引为极限无法取到的len(gas)时,其实是第一个点即0。

classSolution:def canCompleteCircuit(self, gas: List[int], cost: List[int]) ->int:#净营收大于0才能跑完 在此情况下,即是面积的相加,无论怎么加,最后和都是一样的,只是要保证最低点大于0,相当于一个曲线随机开始点,并把整体移动使得第一个点为0.无论中间值是多少,相对大小不变,因此可以直接找到最小点,从该点开始。因为rest指的是到下一个的剩余油量,所以返回i+1

if sum(gas)

min_val= float('inf')for i inrange(len(gas)):

rest+= gas[i]-cost[i]if rest

min_val= rest #注意更新min_val

i_min = i+1

if i_min ==len(gas):

i_min=0return i_min

leetcode135.分发糖果

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。你需要按照以下要求,帮助老师给这些孩子分发糖果:每个孩子至少分配到 1 个糖果。评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。那么这样下来,老师至少需要准备多少颗糖果呢?

思路:先使得1.右边孩子比左边评分高,则比左边孩子多一个糖。2.左边孩子比右边评分高,则左边孩子取(现在的糖,右边孩子糖量+1)的最大值。值得注意的是,加糖时不能破坏之前已经排好的糖量顺序,所以一个值应当先更新再被比。左右比较,若动作为改变右边的,则向右遍历,使得右边的值先更新后再被比;同理,若动作为改变左边的值,则向左遍历。

classSolution:def candy(self, ratings: List[int]) ->int:

candies= [1 for i inrange(len(ratings))]#右>左 向右遍历 右=左+1

for i in range(1,len(candies)):if ratings[i]>ratings[i-1]:

candies[i]= candies[i-1]+1

#左>右 向左遍历 左=max(now,右+1)

for i in range(len(candies)-2,-1,-1):if ratings[i]>ratings[i+1]:

candies[i]= max(candies[i],candies[i+1]+1)return sum(candies)

leetcode860.柠檬水找零

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。注意,一开始你手头没有任何零钱。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

思路:该问题的要点在于实际上只有三种货币:5,10,20.要注意20可以找10+5或者5*3。

leetcode406.根据身高重建队列

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

思路:将人按身高降序(优先),ki升序的顺序排列。新建一个结果,然后从左向右依次将人插入到第ki个位置。重点在于实现这一步,通过list的合并,注意每个元素要以列表的形式合并,res的部分肯定是列表,注意元素部分。

classSolution:def reconstructQueue(self, people: List[List[int]]) ->List[List[int]]:

people.sort(key= lambda x:(x[0],-x[1]), reverse = True) #注意写法

#按身高降序,人数升序排 然后从左到右插入人数位置

res =[]for i inrange(len(people)):

i_insert= people[i][1]

res= res[:i_insert]+[people[i]]+res[i_insert:] #注意

return res

leetcode452.用最小数量的箭引爆气球

在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以纵坐标并不重要,因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足  xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。给你一个数组 points ,其中 points [i] = [xstart,xend] ,返回引爆所有气球所必须射出的最小弓箭数。

思路:在气球右边界放箭,则按右边界升序排列,若某气球左边界小于放箭点,则引爆;大于,则需要另外的箭,并更新放箭点。重点在于为什么按右边界升序:按右边界升序,则后面的元素的右边界必然大于等于该点右边界,则若元素的左边界小于该点右边界,放箭点必然在元素左右之间。核心是通过排序控制一边的相对位置,在判断时只用判断另一边。同理,按左边界升序排列也可以,此时倒序遍历,元素的左边界必然小于该点的左边界,若右边界大于该点左边界,则可放箭。总之,哪个边界放箭,哪个边界排序。

classSolution:def findMinArrowShots(self, points: List[List[int]]) ->int:#按右边界升序,需要判断气球的左边界是否小于维护边界的右边界,是则击中,不是则需要加一个箭

points.sort(key = lambda x:x[1])if notpoints:return0

end= points[0][1]

res= 1

for i in range(1,len(points)):if points[i][0] >end:

res+= 1end= points[i][1]return res

leetcode435.无重叠区间

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

注意:可以认为区间的终点总是大于它的起点。区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。

思路:按右边界升序排列,重叠则数量加一,不重叠则更新右界。因为右边界是升序的,则其留给右边的空间是多的,所以当重叠时优先留下留给空间多的,删除留给空间少的,即右边的选项。

classSolution:def eraseOverlapIntervals(self, intervals: List[List[int]]) ->int:#注意题目要求的区别 需要移除区间的数量

if notintervals:return0

intervals.sort(key= lambda x:x[1])

end= intervals[0][1]

count=0for i in range(1,len(intervals)):if intervals[i][0]

count+= 1

continue

else:

end= intervals[i][1]return count

leetcode736.划分字母区间

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

思路:首先确定每个字母最后出现的索引,用字典存下来。然后遍历字符串,判断在(i_start,i]之间的字母最后出现索引的最大值,当该值等于i时,则(i_start,i]区间外无该区间字母,符合条件。易错点:因为左边是半开区间,所以起始值应当为-1;另外注意题目对返回值的要求,返回值为列表长度,非列表区间。

classSolution:def partitionLabels(self, S: str) ->List[int]:

max_index=0

res=[]

dict_last_index={}for i in range(len(S)): #每个字母最后一次出现的位置

dict_last_index[S[i]] =i#(i_start,i]的字符串

i_start = -1

for i in range(len(S)): #找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点

if dict_last_index[S[i]]>max_index:

max_index=dict_last_index[S[i]]if i ==max_index:

res.append(max_index-i_start)

i_start=max_indexreturn res

leetcode56.合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。

思路:关键是判断区间是否在区间内,还需要合并区间,所以需要从最大的边界开始,因此按左边界升序排列。若两区间重叠,则扩大区间包含范围;若不重叠,则更换区间。如果某区间开始点小于等于当前区间结束点,则说明重叠,所以结束点更新为二者结束点中较大的一个;如果某区间的开始点大于当前区间的结束点,则说明区间不重叠,应当存储当前区间,并开启下一个区间。注意在循环结束时补上一个结果,因为最后一次的结果没有存储。

classSolution:def merge(self, intervals: List[List[int]]) ->List[List[int]]:

intervals.sort()if not intervals: #注意 因为后面直接以0位置初始化,所以要保证0有值

returnintervals

start=intervals[0][0]

end= intervals[0][1]

res=[]for i in range(1,len(intervals)):if intervals[i][0] <=end:

end= max(intervals[i][1],end)else:

res.append([start,end])

start,end= intervals[i][0],intervals[i][1]

res.append([start, end])#注意

return res

leetcode738.单调递增的数字

给定一个非负整数 N,找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增。(当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。)

思路:对于两位数,如果左比右高,则左减一,并将右边置为9。对于多位的情况,我们实际上是要找最高位的“错误”,然后将后面改为9。但不能从左往右直接找,因为先更新后比较,如前面所分析的,需要改左边,则需要从右向左找,因此每一位如果左高右低则左减一继续比较,该操作并不在乎右边变成什么,因为后面统一置为9。

classSolution:def monotoneIncreasingDigits(self, N: int) ->int:#右往左

num =list(str(N))

index=len(num)for i in range(len(num)-2,-1,-1):if int(num[i])>int(num[i+1]):

index= i+1num[i]= str(int(num[i])-1)for i inrange(index,len(num)):

num[i]= '9'

return int(''.join(num))

leetcode968.监控二叉树

给定一个二叉树,我们在树的节点上安装摄像头。节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。计算监控树的所有节点所需的最小摄像头数量。

思路:叶子节点不放摄像头,希望放在叶子节点的父节点,在此基础上放置更上层的摄像头,因此从底下向上放,即使用后序遍历。一个节点有三种可能:被覆盖,本身就有摄像头,和没被覆盖。因为不想在叶子节点安装摄像头,所以空节点返回被覆盖。当左右有未被覆盖的节点时,该节点返回摄像头,总数加一;有摄像头时,返回被覆盖;其他则为未被覆盖。返回值为传入节点的状态,注意最后根节点返回值在总函数内,如果根节点未被覆盖,则加一个摄像头。注意如果返回为摄像头,因为在递归时已经算入摄像头的总数,所以总数不要增加。

classSolution:def minCameraCover(self, root: TreeNode) ->int:

res=0deftraverse(root):#1:被覆盖 2:摄像头 3:未覆盖

nonlocal resif not root: #叶子节点不安摄像头,则空节点需要认为是被覆盖的

return 1condition_left=traverse(root.left)

condition_right=traverse(root.right)if condition_left ==3 or condition_right == 3: #如果有一个没覆盖 则需要摄像头

res += 1

return 2

if condition_left == 2 or condition_right == 2: #如果有一个有摄像头 即被覆盖

return 1

return 3condition_root=traverse(root)if condition_root == 3:

res+= 1

return res

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值