leetcode之贪心算法刷题总结2
Posted nuist__NJUPT
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode之贪心算法刷题总结2相关的知识,希望对你有一定的参考价值。
leetcode之贪心算法刷题总结2
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择,就能得到问题的答案。贪心算法需要充分挖掘题目中条件,没有固定的模式,解决有贪心算法需要一定的直觉和经验。
贪心算法不是对所有问题都能得到整体最优解。能使用贪心算法解决的问题具有「贪心选择性质」。「贪心选择性质」严格意义上需要数学证明。能使用贪心算法解决的问题必须具备「无后效性」,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
1-数组拆分I
题目链接:题目链接戳这里!!!
思路: 对数组进行升序排序,在每两个之间选择一个最小的累加即可,基础贪心。
class Solution
public int arrayPairSum(int[] nums)
Arrays.sort(nums) ;
int ans = 0 ;
for(int i=0; i<nums.length-1; i+=2)
ans += Math.min(nums[i],nums[i+1]) ;
return ans ;
2-用最少数量的箭引爆气球
题目链接:题目链接戳这里!!!
思路:这一题算是一个经典的贪心策略的应用了,想使用最少数量的箭引爆所有的气球,我们可以按照气球结束的直径坐标进行排序,然后对所有的气球的起始坐标进行遍历,如果下一次的起始大于当前的大于当前的结束,说明必须增加一只箭才能引爆,同时需要更新当前气球直径的结束坐标。
class Solution
public int findMinArrowShots(int[][] points)
//按照气球直径结束的坐标进行排序,如果下一次开始大于当前的结束,需要增加一只箭,同时更新当前结束位置
if(points.length==0)
return 0 ;
int ans = 1 ;
Arrays.sort(points, new Comparator<int []>()
public int compare(int [] points1, int [] points2)
if(points1[1]>points2[1])
return 1 ;
else if(points1[1]<points2[1])
return -1 ;
else
return 0 ;
) ;
int p = points[0][1] ;
for(int [] point : points)
if(point[0] > p)
p = point[1] ;
ans ++ ;
return ans ;
3-无重叠区间
题目链接:题目链接戳这里!!!
思路:这一题是上一题的变种题,也是按照区间尾部升序排序,然后如果有区间的开始大于等于尾部,则需要ans++,同时更新区间尾部,最后的ans就是可以保留的最大无重叠区间个数,n-ans则是需要删除的最少区间个数。
class Solution
public int eraseOverlapIntervals(int[][] intervals)
//用ans去记录可以保留的最多区间数,n-ans就是可以删除的最少区间数
int n = intervals.length ;
if(n==0)
return 0 ;
Arrays.sort(intervals,new Comparator<int[]>()
public int compare(int [] intervals1, int [] intervals2)
return intervals1[1] - intervals2[1] ;
) ;
int ans = 1 ;
int pos = intervals[0][1] ;
for(int [] interval : intervals)
if(interval[0] >= pos)
ans ++ ; //计算出能保留最大不重叠区间个数
pos = interval[1] ;
return n - ans ;
4-最短无序连续子数组
题目链接:题目链接戳这里!!!
思路:对原数组进行升序排序,然后将排序后的数组和原数组进行比较,找出左右不相同的位置,相减就是需要排序的最短无序子数组长度。
class Solution
public int findUnsortedSubarray(int[] nums)
int n = nums.length ;
if(n==0)
return 0 ;
int [] sort = new int [n] ;
for(int i=0; i<n; i++)
sort[i] = nums[i] ;
Arrays.sort(sort) ;
int left = 0, right = n-1 ;
for(int i=0; i<n; i++)
if(nums[i]==sort[i])
left++ ;
else
break ;
for(int i=n-1; i>=0; i--)
if(nums[i]==sort[i])
right -- ;
else
break ;
return (right - left + 1) > 0 ? right-left+1 : 0 ;
5-分发饼干
题目链接:题目链接戳这里!!!
思路:对数组g和数组s进行升序排序,然后双指针判断即可。
class Solution
public int findContentChildren(int[] g, int[] s)
int n = g.length ;
int m = s.length ;
if(m==0)
return 0 ;
Arrays.sort(g) ;
Arrays.sort(s) ;
int a=0, b=0 ;
int ans = 0 ;
while(a<n && b<m)
if(g[a]<=s[b])
ans ++ ;
a ++ ;
b ++ ;
else
b ++ ;
return ans ;
6-分发糖果
题目链接:题目链接戳这里!!!
思路:这道题在贪心中不太好理解,其实就是分为两个过程,一次从左向右遍历,一次从右向左遍历,从左向右遍历过程中,如果当前孩子的评分大于前一个,则当前孩子的糖果等于前一个孩子的糖果数量加1,否则当前孩子的糖果数等于,从右向左遍历则类似,依次类推,对比对应的左右统一孩子的最大值即为该孩子的糖果数。
class Solution
public int candy(int[] ratings)
//一次从左向右遍历,一次从右向左遍历
int n = ratings.length ;
int [] left = new int [n] ;
for(int i=0; i<n; i++)
if(i>0 && ratings[i]>ratings[i-1])
left[i] = left[i-1]+1 ;
else
left[i] = 1 ;
int right = 0 ;
int ans = 0 ;
for(int i=n-1; i>=0; i--)
if(i<n-1 && ratings[i] > ratings[i+1])
right ++ ;
else
right = 1 ;
ans += Math.max(left[i],right) ;
return ans ;
7-种花问题
题目链接:题目链接戳这里!!!
思路:计数法,每次中间满足连续的三个则ans累加1,如果左边或者右边有两个,也是可以累加1的。
class Solution
public boolean canPlaceFlowers(int[] flowerbed, int n)
int ans = 0, cnt = 1; //cnt初始化为1,前面两个0也是可以的
for(int i=0; i<flowerbed.length; i++)
if(flowerbed[i]==0)
cnt ++ ;
else
cnt = 0 ;
if(cnt == 3)
ans ++ ;
cnt = 1 ;
if(cnt == 2)
ans ++ ;
return n <= ans ;
8-有效的三角形个数
题目链接:题目链接戳这里!!!
思路:对数组元素进行升序排序,每次固定最长的边,双指针遍历其他边。
class Solution
public int triangleNumber(int[] nums)
int n = nums.length ;
if(n<3)
return 0 ;
int ans = 0 ;
Arrays.sort(nums) ;
for(int i=nums.length-1; i>=2; i--)
int left = 0, right = i-1, k = nums[i] ;
while(left<right)
if(nums[left]+nums[right]>k)
ans += right - left ;
right -- ;
else
left ++ ;
return ans ;
9-划分字母区间
题目链接:题目链接戳这里!!!
思路:用map记录每个字母最后出现的位置,然后从前向后遍历,如果当前位置等于字符串中所有字母的最后出现的那个位置,则截取当前字符串,继续遍历后面的字符串。
class Solution
public List<Integer> partitionLabels(String s)
Map<Character, Integer> map = new HashMap<>() ;
for(int i=0; i<s.length(); i++)
map.put(s.charAt(i),i) ;
List<Integer> ans = new ArrayList<>() ;
int left = 0, right = 0 ;
for(int i=0; i<s.length(); i++)
right = Math.max(right,map.get(s.charAt(i))) ;
if(i==right)
ans.add(right-left+1) ;
left = right + 1 ;
right = 0 ;
return ans ;
10-森林中的兔子
题目链接:题目链接戳这里!!!
思路:这题可以很巧妙的不用hashmap,直接用一维数组,效率更高,其实思路还是一样,两只相同颜色的兔子看到的其他同色兔子数必然是相同的。反之,若两只兔子看到的其他同色兔子数不同,那么这两只兔子颜色也不同。
因此,将answers 中值相同的元素分为一组,对于每一组,计算出兔子的最少数量,然后将所有组的计算结果累加,就是最终的答案。
class Solution
public int numRabbits(int[] answers)
int [] map = new int [1000] ;
int ans = 0 ;
//同种颜色的肯定肯定会报的其它颜色也一样
for(int i=0; i<answers.length; i++)
if(map[answers[i]]>0) //回答颜色已经出现
map[answers[i]] -- ;
else //构建回答的新颜色
map[answers[i]] = answers[i] ;
ans += answers[i] + 1 ;
return ans ;
11-柠檬水找零
题目链接:题目链接戳这里!!!
思路:使用两个变量five和ten模拟商家手上拥有的5和10元的个数,枚举每一种情况,依次判断即可。
class Solution
public boolean lemonadeChange(int[] bills)
int money = 0, five = 0, ten = 0 ;
//模拟商家手头5和10的个数
for(int bill : bills)
if(bill==5)
five ++ ;
else if(bill==10)
ten ++ ;
if(five>=1)
five -- ;
else
return false ;
else if(bill == 20)
if(ten>0 && five>0)
ten -- ;
five -- ;
else if(five>=3)
five -= 3 ;
else
return false ;
return true ;
12-重构字符串
题目链接:题目链接戳这里!!!
思路:依次记录每个字符出现的次数,如果最大的字符出现次数大于(n+1)/2,则不能重构字符串,否则可以重构字符串,在重构字符串的过程中,如果当前字符出现的次数大于0,且小于等于n/2,则可以放到奇数位,只要字符出现次数大于0就可以放到偶数位。
class Solution
public String reorganizeString(String s)
int n = s.length() ;
if(n<2)
return s ;
int maxCount = 0 ;
int [] cnt = new int [26] ;
for(int i=0; i<n; i++)
cnt[(int)(s.charAt(i)-'a')] ++ ;
maxCount = Math.max(maxCount, cnt[s.charAt(i)-'a']) ;
if(maxCount>(n+1)/2)
return "" ;
int oddIndex = 1, evenIndex = 0 ;
char [] ans = new char [n] ;
for(int i=0; i<26以上是关于leetcode之贪心算法刷题总结2的主要内容,如果未能解决你的问题,请参考以下文章
Leetcode刷题100天—452. 用最少数量的箭引爆气球(贪心)—day38