贪心算法part01
day29-1 ●理论基础
什么是贪心
局部最优求全局最优
两个极端
常识性的东西也有方法论
贪心套路
贪心无套路
想局部最优即可
数学归纳、反证法
需要数学证明的题目:环形链表||
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){// 有环
// 找环的入口
ListNode index1 = fast;
ListNode index2 = head;
while(index1 != index2){
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}
硬生生想出来的套路:
- 将问题拆分为若干个子问题
- 找出适合的贪心策略
- 求解每一个问题的最优解
- 将局部最优解堆叠成全局最优解
总结
局部最优得全局最优
有两个极端,所以不要轻视
常识性推导+举反例
day29-2 ●455.分发饼干
解题思路
局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩。
大饼干尽量去喂大胃口的孩子
其他思路:小饼干能满足一个小孩就满足一个小孩
代码实现
class Solution {
// 大饼干尽量去满足大胃口
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int count = 0;// 统计可以满足的孩子数
int start = s.length - 1;
for(int index = g.length - 1; index >= 0; index--){
if(start >= 0 && g[index] <= s[start]){
count++;
start--;
}
}
return count;
}
}
class Solution {
// 小饼干能满足一个小孩子就满足一个小孩子
public int findContentChildren(int[] g, int[] s) {// g:胃口 s:饼干
Arrays.sort(g);
Arrays.sort(s);
int count = 0;
int start = 0;
for(int index = 0; index < s.length; index++){
if(start < g.length && s[index]>= g[start]){
count++;
start++;
}
}
return count;
}
}
遍历顺序
大饼干尽量去喂大胃口的孩子:倒序
小饼干能满足一个小孩就满足一个小孩:正序
day29-3 ●376. 摆动序列
解题思路
上下有平坡
收尾元素
代码实现
class Solution {
// 当出现摆动的时候再去更新prediff,可以绕过有单调有平坡的情况
public int wiggleMaxLength(int[] nums) {
if(nums.length <= 1){
return nums.length;
}
int curDiff = 0;// 当前差值
int preDiff = 0;// 上一个差值
int count = 1; // 末尾也算一个
for(int i = 1; i < nums.length; i++){
curDiff = nums[i] - nums[i - 1];
if((preDiff<=0&&curDiff>0)||(preDiff>=0&&curDiff<0)){
count++;
// 当出现摆动的时候再去更新prediff,可以绕过有单调有平坡的情况
preDiff = curDiff;
}
}
return count;
}
}
单调有平坡
总结
要考虑:1.上下有平坡、2.单调有平坡的情况
day29-4 ●53. 最大子序和
解题思路
只要前面的连续和不为负数都有可能让后面的数变大
如果前面的连续后为负的话就立刻抛弃选择下一个位置作为我们的起始位置
模拟过程
代码实现
class Solution {
public int maxSubArray(int[] nums) {
int result = Integer.MIN_VALUE;
int count = 0 ;// 前面的连续和
for(int i = 0; i < nums.length; i++){
count += nums[i];
// 统计最大值
if(count>result){
result = count;
}
// 如果前面的连续和为负,对整体值没有贡献,直接舍弃
if(count<0){
count=0;
}
}
return result;
}
}
总结
想清楚贪心贪的是哪里,局部最优是哪里
不是遇到负数就跳过而是前面的连续和为负数我们才选择下一个元素作为我们的起点