题解-LeetCode-贪心算法

1. 贪心算法概述

贪心算法(又称贪婪算法)是指,通过局部最优策略以达成全局最优解。

贪心算法思路:

分解
求解
相加
分解
求解
相加
分解
求解
相加
分解
求解
相加
开始
总体问题
局部子问题1
局部最优解1
全局最优解
局部子问题2
局部最优解2
局部子问题...
局部最优解...
局部子问题n
局部最优解n
结束

2. 分配问题

2.1 LeetCode-No.455-Easy

解题思路:
总体目的: 使尽可能多的孩子吃到饼干。
局部子问题: 在吃饱的前提下,每个孩子只吃一个尺寸最小的饼干。
总体流程: 当前孩子中胃口最小的孩子应优先吃饼干。将孩子们按胃口由小到大的顺序排序,进行一次遍历(吃饼干)。
算法终止条件: 遍历完成后算法即终止,此时每个孩子都吃到了饼干或者饼干全部被吃完。

2.2 LeetCode-No.135-Hard

解题思路:
总体目的: 使所有孩子满足如下公式:
c a n d y [ i ] = m a x ( c a n d y [ i − 1 ] , c a n d y [ i + 1 ] ) + 1 , i f   r a t i n g s [ i ] > r a t i n g s [ i − 1 ]   a n d   r a t i n g s [ i ] > [ i − 1 ] (2.2.1) candy[i]=max(candy[i-1],candy[i+1])+1, \\if\ ratings[i] >ratings[i-1]\ and\ ratings[i] >[i-1]\tag{2.2.1} candy[i]=max(candy[i1],candy[i+1])+1,if ratings[i]>ratings[i1] and ratings[i]>[i1](2.2.1)
其中 c a n d y candy candy数组中的元素均初始化为1,求 s u m ( c a n d y ) sum(candy) sum(candy)
局部子问题: 根据上述公式,可以分解如下为两个子问题:
子问题1:对于每一个孩子,应满足:
c a n d y [ i ] − c a n d y [ i − 1 ] ≥ 1 , i f   r a t i n g s [ i ] > r a t i n g s [ i − 1 ] (2.2.2) candy[i]-candy[i-1]\ge1, \\if\ ratings[i] >ratings[i-1]\tag{2.2.2} candy[i]candy[i1]1,if ratings[i]>ratings[i1](2.2.2)
子问题2:对于每一个孩子,应满足:
c a n d y [ i ] − c a n d y [ i + 1 ] ≥ 1 , i f   r a t i n g s [ i ] > r a t i n g s [ i − 1 ] (2.2.3) candy[i]-candy[i+1]\ge1, \\if\ ratings[i] >ratings[i-1]\tag{2.2.3} candy[i]candy[i+1]1,if ratings[i]>ratings[i1](2.2.3)
总体流程: 两次遍历,每次遍历各解决一个子问题。
遍历1:从前向后遍历,解决子问题1。
遍历2:从后向前遍历,解决子问题2。
遍历时,首端和尾端的孩子需特殊处理。
两次遍历,使得每一个孩子与其相邻的两个孩子各进行了一次比较,比较完成后均满足公式2.2.1。
算法终止条件: 两次遍历完成后算法即终止,此时求出 s u m ( c a n d y ) sum(candy) sum(candy)即为输出的结果。

2.3 LeetCode-No.605-Easy

解题思路:
总体目的: 在花坛中种植尽可能多的花,理论上,一个空花坛中能种植的最大数量如下:
⌈ f l o w e r b e d . l e n g t h / 2 ⌉ (2.3.1) \lceil flowerbed.length/2\rceil\tag{2.3.1} flowerbed.length/2(2.3.1)
局部子问题: 若花坛未种花,且其相邻的花坛均为种花,则将该花坛种植上花,否则跳过。
总体流程: 从前向后对所有花坛进行一次遍历。
算法终止条件: 遍历完成后算法即终止,此时可知该花坛中还能种植的最大数量。

2.4 LeetCode-No.122-Easy

解题思路:
总体目的: 使获得的利润最大。
局部子问题: 若前一天的股票价格低于今天,则应当完成一次买卖交易(前一天买入,今天卖出)。由于不限制交易次数,可以在某天卖出后再买入,因此这种策略不会影响最终结果。例:三天的股票价格为 [ 1 , 2 , 5 ] [1,2,5] [1,2,5],则在第一天买入->第二天卖出->第二天再买入->第三天卖出的利润和在第一天买入->第三天卖出的利润是相同的: ( 2 − 1 ) + ( 5 − 2 ) = 5 − 1 = 4 (2-1)+(5-2)=5-1=4 (21)+(52)=51=4
总体流程: 是否能进行一次买卖交易,需要比较当天和前一天的价格,则应从第二天开始对股票价格进行一次遍历。
算法终止条件: 遍历完成后算法即终止。

2.5 LeetCode-No.406-Medium

解题思路:
总体目的: 按要求重建队列。
局部子问题: 插入新的人员时,若此人前面有 k k k个人比他高,则重建的索引位置之前必须预留 k k k个位置给比他高的人。
总体流程: 优先插入身高较高的人,则插入一个新的人员时,由于比他高的人员都已经插入到队列中,因此只需要将该人员插入到索引位置 k k k处,后面人员的位置相应往后顺延即可。将输入数组 p e o p l e people people按身高由大到小、 k k k值由小到大的顺序排序,进行一次遍历,对于Java可采用List容器,遍历时直接将当前人员插入到容器的索引位置 k k k处,后面人员的位置会自动往后顺延。
算法终止条件: 遍历完成后算法即终止,将容器转为二维数组作为输出。

2.6 LeetCode-No.665-Medium

解题思路:
总体目的: 使输入数组变为非递减数组的改变次数尽可能少。
局部子问题: 若在索引位置 i i i处出现 n u m s [ i − 1 ] > n u m s [ i ] nums[i-1]>nums[i] nums[i1]>nums[i]的情况说明需要改变一次元素,改变的元素可以是 n u m s [ i − 1 ] nums[i-1] nums[i1] n u m s [ i ] nums[i] nums[i],由于数组是非递减的,改动的思路是尽量将 n u m s [ i − 1 ] nums[i-1] nums[i1]改为较小的值,考虑到各种情况,元素的改动公式如下:
{ n u m s [ i − 1 ] = n u m s [ i ] nums[i-2]不存在 n u m s [ i − 1 ] = n u m s [ i − 2 ] n u m s [ i − 2 ] ≤ n u m s [ i ] n u m s [ i ] = n u m s [ i − 1 ] n u m s [ i − 2 ] > n u m s [ i ] (2.6.1) \begin{cases} nums[i-1]=nums[i]& \text{nums[i-2]不存在}\\ nums[i-1]=nums[i-2]& nums[i-2]\le nums[i]\\ nums[i]=nums[i-1]& nums[i-2]>nums[i] \end{cases}\tag{2.6.1} nums[i1]=nums[i]nums[i1]=nums[i2]nums[i]=nums[i1]nums[i-2]不存在nums[i2]nums[i]nums[i2]>nums[i](2.6.1)
总体流程: 从第二个元素开始对输入数组进行一次遍历,每当需要改变时,需改变一个数组元素后才能继续遍历,因为改变后的数组元素将会影响到后续数组元素的判断。
算法终止条件: 遍历完成或已经确定改变次数大于1时算法即终止,另外,如果数组长度为1,则该数组天然就是非递减数组,不需要改变,不会进行遍历。

3. 区间问题

3.1 LeetCode-No.435-Medium

解题思路:
总体目的: 尽量多保留不重叠的区域,使需要被移除的区间最少。
局部子问题: 若上一个区间与当前区间不相交,则保留当前区间,否则移除当前区间。
总体流程: 优先保留结尾较小的区间,因为结尾越小的区间留给其他区间的余地越大。将所有区间按结尾由小到大的顺序排序,进行一次遍历。其中,排序后第一个区间的结尾最小,必定保留。
算法终止条件: 遍历完成后算法即终止,此时可知被移除的区间数量。

3.2 LeetCode-No.452-Medium

解题思路:
总体目的: 若两个区间相交,则定义它们属于同一个区间块。本题的目的是使需要的区间块数量尽可能少。
局部子问题: 区间 [ a , b ] [a,b] [a,b]与当前区间块(结尾为 x x x)比较,若满足: x ≥ a x\ge a xa,说明该区间属于当前区间块,否则说明当前区间块已结束,该区间属于一个新的区间块(结尾为 b b b)。
总体流程: 若根据区间块的结尾判断各区间的相交关系,则所有区间需按结尾由小到大的顺序排序,以排序后第一个区间的结尾作为第一个区间块的结尾,进行一次遍历。
算法终止条件: 遍历完成后算法即终止,此时可知需要的区间块数量。

3.3 LeetCode-No.763-Medium

解题思路:
总体目的: 字符串数组 [ a 1 , a 2 , a 3 , . . . , a n ] [a_1,a_2,a_3,...,a_n] [a1,a2,a3,...,an]中的字母在字符串中最后出现的位置索引为 [ x 1 , x 2 , x 3 , . . . , x n ] [x_1,x_2,x_3,...,x_n] [x1,x2,x3,...,xn],使划分的片段尽可能多。
局部子问题: 一旦满足划分要求,则划分出一个片段。字母 a a a(最后出现的位置索引为 x x x)与当前片段(区间为 [ x s t a r t , x e n d ] [x_{start},x_{end}] [xstartxend])比较,若 x = x e n d x= x_{end} x=xend,说明字母 a a a已被该片段完全包含,后面没有重复的字母 a a a,该片段结束,否则应将 x e n d x_{end} xend更新为 x x x使该片段包含当前的字母 a a a
总体流程: 统计各字母在字符串中最后出现的位置索引,再对字符串数组进行一次遍历。第一个片段初始区间为 [ 0 , 0 ] [0,0] [0,0],若一个片段在索引 i i i处结束,则下一个片段初始区间为 [ i + 1 , i ] [i+1,i] [i+1,i]
算法终止条件: 遍历完成后算法即终止,对于每一个片段,在遍历中可由片段最终区间 [ x s t a r t , x e n d ] [x_{start},x_{end}] [xstartxend]得知该片段的长度为: x e n d − x s t a r t + 1 x_{end}-x_{start}+1 xendxstart+1

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值