目录
1、贪心算法介绍
1)贪婪算法(贪心算法)是指在对问题进行求解时,在每一步选择中都采取最好或者最优(即最有利)的选择,从而希望能够导致结果是最好或者最优的算法。
2)贪婪算法所得到的结果不一定是最优的结果(有时候会是最优解),但是都是相对近似(接近)最优解的结果。
2、leetcode题分析
2.1、最长回文串
题目要求
给定一个包含大写字母和小写字母的字符串 s ,返回通过这些字母构造成的 最长的回文串。在构造过程中,请注意区分大小写 。比如 "Aa" 不能当做一个回文字符串。例如:
输入:s = "abccccdd"
输出:7
解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
题目分析
已知回文串的是指正着读和反着读都一样的字符串,我们可以将其看作是一个对称模型。在一个回文串中,只有最多一个字符出现奇数次,其余的字符都只能是偶数次。
若字符串s中每种字符的数量都是偶数,那么必然能形成回文串,因此直接输出字符串s的长度即可;
若字符串s中有字符的数量为奇数,为了能够凑成回文串,我们可以先默认奇数没办法形成回文串,用 “字符串s的长度” 减去 “数量为奇数次的字符种类数” ,亦可理解为将奇数次的字符数量减一变成偶数次后重新计算字符串s的长度。这么做会多出数个字符,我们可以取其一将其作为回文串对称模型的中心,即在上述的结果加一得到最长的回文串。
代码演示
public static int longestPalindrome2(String s) {
// 找出可以构成最长回文串的长度
int[] arr = new int[128];
for(char c : s.toCharArray()) {
//记录每一种字符的数量
arr[c]++;
}
int j = 0;
for (int i : arr) {
j += (i % 2);
}
/*
只要有一个字符的数量是奇数,那么总数就一定是奇数
由于奇数没法放两边形成回文串,因此全部减1形成偶数,即减去奇数的种类数 s.length() - j
但由于是奇数,可以取一个字符放回文串中间,因此长度为s.length() - j + 1
*/
return j == 0 ? s.length() : (s.length() - j + 1);
}
2.2、分发饼干
题目要求
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。 对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。例如:
输入:
g = [1,2,3],s = [1,1]
输出:1
解释::你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。 虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 所以你应该输出1。
题目分析
目的是为了尽可能满足越多数量的孩子,由题意可知胃口值越小的孩子越容易满足,因此优先满足胃口值小的孩子,即对数组g进行从小到大排序。根据贪心算法原理,我们应该选择可以满足这个孩子的胃口且尺寸最小的饼干,因此饼干的数组s也需要从小到大排序。
代码演示
public static int findContentChildren(int[] g, int[] s) {
int gk = 0;
int count = 0;
//从小到大排序
Arrays.sort(g);
Arrays.sort(s);
for (int i = 0; i < s.length; i++) {
//拿出最小的饼干,从胃口最小的孩子开始遍历
//由于尽可能满足最多孩子,因此将胃口由小到大的孩子喂饱最容易满足要求
if(s[i] >= g[gk]){
count++;
gk++;
}
if(gk > g.length-1){
break;
}
}
return count;
}
2.3、数组拆分
题目要求
给定长度为 2n 的整数数组 nums ,你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从 1 到 n 的 min(ai, bi) 总和最大。 返回该最大总和 。例如:
输入:nums = [1,4,3,2]
输出:4
解释:所有可能的分法(忽略元素顺序)为:
1、(1, 4), (2, 3) -> min(1, 4) + min(2, 3) = 1 + 2 = 3
2、(1, 3), (2, 4) -> min(1, 3) + min(2, 4) = 1 + 2 = 3
3、(1, 2), (3, 4) -> min(1, 2) + min(3, 4) = 1 + 3 = 4
所以最大总和为 4
题目分析
min(a,b)方法即取a、b中的最小值。让数组中的数字两两组合取最小值,我们应该尽可能保证组合的值最为接近,这样才能保证相加后返回的是最大总和。因此将数组进行从小到大排序后进行组合。由于组合后取出的是最小值,那么只需要取偶数下标的数进行相加即可。
代码演示
public static int arrayPairSum(int[] nums) {
/**
* 数组:1 2 2 5 6 6
* 下标:0 1 2 3 4 5
* * * *
* 1+2+6 = 9
* 从小到大排序后取偶数下标
*/
Arrays.sort(nums);
int sum = 0;
for (int i = 0; i < nums.length; i++) {
if(i % 2 != 1){
sum+=nums[i];
}
}
return sum;
}
2.4、种花问题
题目要求
假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false。
示例一
输入:
flowerbed = [1,0,0,0,1], n = 1
输出:true
示例二
输入:
flowerbed = [1,0,0,0,1], n = 2
输出:false
题目分析
(1)对于中间的0区间:
1~2个0:可种0朵;
3~4个:可种1朵;
5~6个:可种2朵;
...
count个:可种 (count-1)/2 朵
(2)对于两头的0区间,由于左边、右边分别没有1的限制,可种花朵数稍有不同。为了代码流程的统一,可以在数组最左边、数组最右边分别补1个0,意味着花坛左边、右边没有花。这样公式就跟(1)相同了。
代码演示
示例一
public static boolean canPlaceFlowers2(int[] flowerbed, int n) {
if (flowerbed == null || flowerbed.length == 0) return n == 0;
int countOfZero = 1; // 当前全0区段中连续0的数量,刚开始预设1个0,因为开头花坛的最左边没有花,可以认为存在一个虚无的0
int canPlace = 0; // 可以种的花的数量
for (int bed : flowerbed) {
if (bed == 0) { // 遇到0,连续0的数量+1
countOfZero++;
} else { // 遇到1,结算上一段连续的0区间,看能种下几盆花:(countOfZero-1)/2
canPlace += (countOfZero-1)/2;
if (canPlace >= n) return true;
countOfZero = 0; // 0的数量清零,开始统计下一个全0分区
}
}
// 最后一段0区还未结算:
countOfZero++; // 最后再预设1个0,因为最后花坛的最右边没有花,可以认为存在一个虚无的0
canPlace += (countOfZero-1)/2;
return canPlace >= n;
}
示例二——暴力罗列每一种情况,仅供参考
public static boolean canPlaceFlowers(int[] flowerbed, int n) {
int count = 0;
int length = flowerbed.length;
if(length ==1){
if(flowerbed[0] == 0){
count++;
}
}else {
if(flowerbed[0] == 0 && flowerbed[1] == 0){
count++;
flowerbed[0] = 1;
}
if(flowerbed[length-1] == 0 && flowerbed[length-2] == 0){
count++;
flowerbed[length-1] = 1;
}
}
for (int i = 0; i < length; i++) {
if(flowerbed[i] == 0 && i != 0 && i != length-1 && length!=1){
if(flowerbed[i-1] == 0 && flowerbed[i+1] == 0){
count++;
flowerbed[i] = 1;
}
}
}
if(n <= count ){
return true;
}else {
return false;
}
}