贪心算法题解合集(Java)

79 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情

持续更新中(思路写在代码注释里)

1、最长回文串

给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。

在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。

注意:假设字符串的长度不会超过 1010。

示例 1: 输入: "abccccdd"

输出: 7

解释: 我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。

class Solution {
	
	//出现次数为偶数的字符,可以全部用在回文数上;
	//出现次数为奇数的字符,其中最多的字符可以放在回文数中间,其它的字符可以减去一个(取偶数个),放在回文数里
	public int longestPalindrome(String s) {
 
        int[] count = new int[128];
        //记录下每个字符出现的次数
        for (int i = 0; i < s.length(); ++i) {
            char c = s.charAt(i);
            count[c]++;
        }
 
        int ans = 0;
        for (int v: count) {
        	//这个小技巧可以使 偶数不变,奇数-1
            ans += v / 2 * 2;
            if (v % 2 == 1 && ans % 2 == 0) {
                ans++;
            }
        }
        return ans;
    }
}

2、分发饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

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

示例 1:

输入: g = [1,2,3], s = [1,1] 输出: 1 解释: 你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。 虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 所以你应该输出1。

示例 2:

输入: g = [1,2], s = [1,2,3] 输出: 2 解释: 你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。 你拥有的饼干数量和尺寸都足以让所有孩子满足。 所以你应该输出2.

提示:

1 <= g.length <= 3 * 104 0 <= s.length <= 3 * 104 1 <= g[i], s[j] <= 231 - 1

(1)解法一:穷举,找出每个饼干所能满足的最大的小孩

class Solution {
	
	
	public static int findContentChildren(int[] g, int[] s) {
 
		ArrayList<Integer> as = new ArrayList<>();	//用来存放挑选出来的胃口小于当前饼干尺寸的小孩胃口
		ArrayList<Integer> gs = new ArrayList<>();	//用来储存饥饿的小孩
		
		int count = 0;	//用来统计喂饱的小孩数量
		
		//遍历g,将数据存储到gs中
		for(int g1 : g) {
			gs.add(g1);
		}
		
		
		for(int i=0; i<s.length; i++) {
			for(int g2 : gs) {
				//如果小孩胃口小于饼干尺寸,就放入as中
				if(s[i] >= g2) {
					as.add(g2);
				}
			}
			//没有小孩满足,直接跳过后面的操作
			if(as.isEmpty())
				continue;
			
			int big = as.get(0);
			for(int g3 : as) {
				//贪心,找出能够满足的胃口最大的小孩
				if(g3 > big) {
					big = g3;
				}
			}
			count++;
			//将满足的小孩从饥饿列表中删除
			gs.remove(gs.indexOf(big));
			as.clear();
		}
		return count;
    }
}

(2)解法二:先将两个数组排序,再对应分配,效率会变高

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int numOfChildren = g.length, numOfCookies = s.length;
        
        int count = 0;	//记录喂饱的孩子数量
        
        //s和g任意一个遍历完成,即结束。代表着要么孩子都已喂饱,要么饼干已经用完
        for (int i = 0, j = 0; i < numOfChildren && j < numOfCookies; i++, j++) {
        	//跳过不能满足小孩的饼干
            while (j < numOfCookies && g[i] > s[j]) {
                j++;
            }
            if (j < numOfCookies) {
                count++;
            }
        }
        return count;
    }
}

3、种花问题

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false。

示例 1:

输入:flowerbed = [1,0,0,0,1], n = 1 输出:true

示例 2:

输入:flowerbed = [1,0,0,0,1], n = 2 输出:false

提示:

1 <= flowerbed.length <= 2 * 104 flowerbed[i] 为 0 或 1 flowerbed 中不存在相邻的两朵花 0 <= n <= flowerbed.length