Leedcode算法专题训练(贪心)
1. 分配饼干
题目描述:每个孩子都有一个满足度 grid,每个饼干都有一个大小 size,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足。求解最多可以获得满足的孩子数量。
class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int i=0,j=0;
while(i<g.length && j<s.length){
if(g[i]<=s[j]){
i++;
j++;
}
else{
j++;
}
}
return i;
}
}
2. 不重叠的区间个数
这个是经典的贪心算法,典型的不能再典型
先计算最多能组成的不重叠区间个数,然后用区间总个数减去不重叠区间的个数。
核心思想就是选结束时间最早的。
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
int n = intervals.length;
return n - intervalSchedule(intervals);
}
public int intervalSchedule(int[][] intvs){
if(intvs.length==0)return 0;
Arrays.sort(intvs,new Comparator<int[]>(){
public int compare(int[] a,int[] b){
return a[1]-b[1];
}
});
int count=1;
int x_end=intvs[0][1];
for(int[] interval:intvs){
int start=interval[0];
if(start>=x_end){
count++;
x_end=interval[1];
}
}
return count;
}
}
学习一下lamada表达式
注意大数的问题
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
int n = intervals.length;
return n - intervalSchedule(intervals);
}
public int intervalSchedule(int[][] intvs){
if(intvs.length==0)return 0;
Arrays.sort(intvs, (a,b)->(a[1]-b[1]));
int count=1;
int x_end=intvs[0][1];
for(int[] interval:intvs){
int start=interval[0];
if(start>=x_end){
count++;
x_end=interval[1];
}
}
return count;
}
}
3. 投飞镖刺破气球
题目描述:气球在一个水平数轴上摆放,可以重叠,飞镖垂直投向坐标轴,使得路径上的气球都被刺破。求解最小的投飞镖次数使所有气球都被刺破。
也是计算不重叠的区间个数,不过和 Non-overlapping Intervals 的区别在于,[1, 2] 和 [2, 3] 在本题中算是重叠区间。

Arrays.sort(intervals, Comparator.comparingInt(o -> o[1]));
class Solution {
public int findMinArrowShots(int[][] points) {
int n=points.length;
if(n==0)return 0;
Arrays.sort(points,(a,b)->(a[1]-b[1]));
int count=1;
int end=points[0][1];
for(int[] arr:points){
int start=arr[0];
if(start>end){
count++;
end=arr[1];
}
}
return count;
}
}
4. 根据身高和序号重组队列
注意点为高个子是无法注意矮个子的,并且矮个子无法改变高个子的身高索引数值k
身高 h 降序、个数 k 值升序,然后将某个学生插入队列的第 k 个位置中。
class Solution {
public int[][] reconstructQueue(int[][] people) {
if(people==null||people.length==0||people[0].length==0){
return new int[0][0];
}
Arrays.sort(people,(a,b)->(a[0]==b[0]?a[1]-b[1]:b[0]-a[0]));
List<int[]>queue=new ArrayList<>();
for(int[] p:people){
queue.add(p[1],p);
}
return queue.toArray(new int[queue.size()][]);
}
}
5. 买卖股票最大的收益
class Solution {
public int maxProfit(int[] prices) {
int max=0;
for(int i=0;i<prices.length;i++){
for(int j=i;j<prices.length;j++){
int index=prices[j]-prices[i];
if(max<index)max=index;
}
}
return max;
}
}
只要记录前面的最小价格,将这个最小价格作为买入价格,然后将当前的价格作为售出价格,查看当前收益是不是最大收益。
public int maxProfit(int[] prices) {
int n = prices.length;
if (n == 0) return 0;
int soFarMin = prices[0];
int max = 0;
for (int i = 1; i < n; i++) {
if (soFarMin > prices[i]) soFarMin = prices[i];
else max = Math.max(max, prices[i] - soFarMin);
}
return max;
}
6. 买卖股票的最大收益 II
对于 [a, b, c, d],如果有 a <= b <= c <= d ,那么最大收益为 d - a。而 d - a = (d - c) + (c - b) + (b - a) ,因此当访问到一个 prices[i] 且 prices[i] - prices[i-1] > 0,那么就把 prices[i] - prices[i-1] 添加到收益中。
class Solution {
public int maxProfit(int[] prices) {
if(prices.length==0)return 0;
int max = 0;
for(int i = 1; i < prices.length; i++){
if(prices[i]>prices[i - 1]){
max += prices[i] - prices[i - 1];
}
}
return max;
}
}
7. 种植花朵

public class Solution {
public boolean canPlaceFlowers(int[] flowerbed, int n) {
int i = 0, count = 0;
while (i < flowerbed.length) {
if (flowerbed[i] == 0 && (i == 0 || flowerbed[i - 1] == 0) && (i == flowerbed.length - 1 || flowerbed[i + 1] == 0)) {
flowerbed[i] = 1;
count++;
}
i++;
}
return count >= n;
}
}
8. 判断是否为子序列
class Solution {
public boolean isSubsequence(String s, String t) {
if(s.length()==0)return true;
int i = 0,j = 0;
while(j<t.length() && i<s.length()){
if(s.charAt(i)==t.charAt(j)){
i++;
}
j++;
}
if(i==s.length())return true;
return false;
}
}
public boolean isSubsequence(String s, String t) {
int index = -1;
for (char c : s.toCharArray()) {
index = t.indexOf(c, index + 1);
if (index == -1) {
return false;
}
}
return true;
}
9. 修改一个数成为非递减数组
思路要清洗,具体分析多种情况
这道题给了我们一个数组,说我们最多有1次修改某个数字的机会,
问能不能将数组变为非递减数组。题目中给的例子太少,不能覆盖所有情况,我们再来看下面三个例子:
4,2,3
-1,4,2,3
2,3,3,2,4
我们通过分析上面三个例子可以发现,当我们发现后面的数字小于前面的数字产生冲突后,
- 有时候需要修改前面较大的数字(比如前两个例子需要修改4),
- 有时候却要修改后面较小的那个数字(比如前第三个例子需要修改2),
那么有什么内在规律吗?是有的,判断修改那个数字其实跟再前面一个数的大小有关系,
首先如果再前面的数不存在,比如例子1,4前面没有数字了,我们直接修改前面的数字为当前的数字2即可。
而当再前面的数字存在,并且小于当前数时,比如例子2,-1小于2,我们还是需要修改前面的数字4为当前数字2;
如果再前面的数大于当前数,比如例子3,3大于2,我们需要修改当前数2为前面的数3。
class Solution {
public boolean checkPossibility(int[] nums) {
int cnt=0;
for(int i=1; i < nums.length && cnt < 2; i++){
if(nums[i]>=nums[i-1])continue;
cnt++;
if(i-2>=0 && nums[i-2] > nums[i]){
nums[i]=nums[i-1];
}
else{
nums[i-1]=nums[i];
}
}
return cnt <= 1;
}
}
10. 子数组最大的和
梦开始的题

class Solution {
public int maxSubArray(int[] nums) {
if(nums == null ||nums.length == 0){
return 0;
}
int presum=nums[0];
int maxsum=presum;
for(int i=1; i<nums.length; i++){
presum=presum>0?presum+nums[i]:nums[i];
maxsum=Math.max(maxsum,presum);
}
return maxsum;
}
}
思路
假设[i,j]为我们所寻找的子序列
nums[left,right]考虑任何连续子数组nums[i,j]必然位于下列三种情况 method(getMaxSubArray(int[] nums,int left,int right)
1. 完全位于子数组nums[left,mid]中,因此left<=i<=j<=mid
2. 完全位于子数组nums[mid+1,right]中,因此mid+1<=i<=j<=right
3. 跨越了中点,因此left<=i<=mid<=j<=right
4. 比较三种情况 返回最大值
关键在于情况3 如何对于跨越中点子序列求解(其实并不难) 其实就是两侧最大子序列和相加 method public int midSumMax(int left,int mid,int right,int[] nums)
1. 求出以mid为定点,从mid down to left 的子序列 [i,mid] 也就是leftMaxSum;
2. 求出以mid+1为定点,从mid+1 up to right 的子序列 [mid+1,j] 也就是RightMaxSum;
3. 求和leftMaxSum+RightMaxSum 从而得出中间最大子序列和
class Solution {
public int maxSubArray(int[] nums) {
return getMaxSubArray(nums,0,nums.length-1);
}
public int getMaxSubArray(int[] nums,int left,int right){
if (left==right){
return nums[left];
}else{
int mid=(left+right)/2;
int leftMax=getMaxSubArray(nums,left,mid);//求左边子序列的最大值(情况1)
int rightMax=getMaxSubArray(nums,mid+1,right);//求右边子序列的最大值(情况2)
int midMax=midSumMax(left,mid,right,nums);//求跨越终点子序列最大值(情况3)
//逐一进行比较,返回最大值
if (leftMax>=rightMax&&leftMax>=midMax){
return leftMax;
}else if (rightMax>=leftMax&&rightMax>=midMax){
return rightMax;
}else{
return midMax;
}
}
}
//跨越中点子序列求解
public int midSumMax(int left,int mid,int right,int[] nums){
int Sum=0;
int leftMaxSum=Integer.MIN_VALUE;
//mid为定点,左边最大子序和
for (int i=mid;i>=left;i--){
Sum+=nums[i];
leftMaxSum= Math.max(leftMaxSum,Sum);
}
Sum=0;
//mid+1为顶点,右边最大子序和
int rightMaxSum=Integer.MIN_VALUE;
for (int i=mid+1;i<=right;i++){
Sum+=nums[i];
rightMaxSum=Math.max(rightMaxSum,Sum);
}
return leftMaxSum+rightMaxSum;
}
}
11. 分隔字符串使同种字符出现在一起

class Solution {
public List<Integer> partitionLabels(String S) {
int[] last = new int[26];
for (int i = 0; i < S.length(); ++i)
last[S.charAt(i) - 'a'] = i;
int j = 0, anchor = 0;
List<Integer> ans = new ArrayList();
for (int i = 0; i < S.length(); ++i) {
j = Math.max(j, last[S.charAt(i) - 'a']);
if (i == j) {
ans.add(i - anchor + 1);
anchor = i + 1;
}
}
return ans;
}
}

浙公网安备 33010602011771号