贪心题况详情

Posted by lily on April 7, 2023

摆动数列

求摆动 子序列 的 最大长度 问题:

  1. 摆动子序列 :如何衡量是否摆动
  2. 最大长度:如何遍历一遍找到最大长度

解决思路:

  1. 记录上两个数之间的差值,规定等于 i -(i+1),大于零则为上坡,小于零则为下坡,若能保持上下坡交替关系期间的子序列为 摆动子序列。
    1. 由此可以看出必须使用两个变量a, b 一个记录上一段坡度是否与当前坡度【相反】
    2. 没做出来之前一直执着于用一个变量记录,发现搞得非常混乱,因为当前坡度为可能有三种情况 >0, <0, ==0 而上一个坡度
  2. 假若抛弃前面段选出的的摆动子序列,而后几段的更短,则无法找到之前放弃记录的数据,用数组保存太占空间,可以选用 常数 保存,一直比较让该变量只记录最大值
    class Solution {
     public int wiggleMaxLength(int[] nums) {
         int n = nums.length;
         if (n<2) return n;
    
         int preDiff = nums[0] - nums[1]; // 第一个坡度
         int ret = (preDiff != 0) ? 2 : 1; // 取不取第二个数取决于第一个坡度是否为0
            
         // 从第二个坡度开始遍历(第一个坡度已算出)
         for (int i=2; i<n ; i++){
             // 第二个坡度
             int diff = nums[i-1] - nums[i];
             // 出现升降坡度情况则保存当前数字
             if ((diff>0 && preDiff<=0) || (diff<0 && preDiff>=0)){
                 ret++;
                 // 更新坡度
                 preDiff = diff; // 注意这里,只在摆动变化的时候更新prediff 
             }
         }
         return ret;
     }
    }
    

    跳跃游戏–步数抽象化

  3. 使能向前的步数覆盖的范围尽可能的大
  4. 用最少的步数增大覆盖范围

    跳跃游戏Ⅱ

    贪心思路:在最大跳跃覆盖范围内跳到此范围内使_下一个覆盖范围最大_的点上i 具体实现:

  5. 范围控制:如何找到该范围内最大的跳转点i,并只在此时让步数ans++
  6. 利用当前覆盖最远范围当作右边界,左边界无视,数组只需一直向前遍历即可
  7. 当遍历达到右边界时转跳点随之确定(范围内结束),此时更新当前最大覆盖范围,并使ans++
  8. nextDistance不需要每次清零是因为,nextDis的值不止由nums[i]的数值确定,还由当前下标决定,需要清零的范围已超出i原本的触及范围。

启示: 要控制一个变量阶段性确定为一个值但这个值在反复跳转时,可以考虑用一个暂时变量next/pre/temp 代替该变量变化,最后再赋值给该变量。

// 版本一
class Solution {
public:
int jump(vector<int>& nums) {
    if (nums.size() == 1) return 0;
    int curDistance = 0;    // 当前覆盖最远距离下标
    int ans = 0;            // 记录走的最大步数
    int nextDistance = 0;   // 下一步覆盖最远距离下标
    for (int i = 0; i < nums.size(); i++) {
        nextDistance = max(nums[i] + i, nextDistance);  // 更新下一步覆盖最远距离下标
        if (i == curDistance) {                         // 遇到当前覆盖最远距离下标
            if (curDistance != nums.size() - 1) {       // 如果当前覆盖最远距离下标不是终点
                ans++;                                  // 需要走下一步
                curDistance = nextDistance;             // 更新当前覆盖最远距离下标(相当于加油了)
                if (nextDistance >= nums.size() - 1) break; // 下一步的覆盖范围已经可以达到终点,结束循环
            } else break;                               // 当前覆盖最远距离下标是集合终点,不用做ans++操作了,直接结束
        }
    }
    return ans;
}
};

加油站(找起点问题)

  1. 从起始点到终点整个过程中邮箱里的油量不能<0 , 一旦小于零即中断,所以可以局部油量剩余判断该点能否作为起始点
  2. 所以一遇到起始点到当前油箱内余量<0的情况时立即更换起始点为i+1,且重新开始计算curSum = 0;
  3. 其他问题:
    1. 倘若在选定区间内可以有另一点使得curSum > 0呢?即这种选择起点的局部方法是否存在问题?
    2. 如何判断当前能否走完一圈,应当返回-1还是找到的起点start

IMG_1325.PNG

柠檬水找零–找零问题

身高排队问题

想如何确定一个维度,然后再按照另一个维度重新排列 (套路):一般这种数对,还涉及排序的,根据第一个元素正向排序,根据第二个元素反向排序,或者根据第一个元素反向排序,根据第二个元素正向排序,往往能够简化解题过程。

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        LinkedList<int[]> que = new LinkedList();

        Arrays.sort(people, (a, b)->{
            if (a[0] == b[0]) return a[1] - b[1]; // 人数按默认从小到大
            return -(a[0]-b[0]); // 身高按从大到小
        });

        for (int[] p:people){
            que.add(p[1], p);  // 将k=i 的数插入i位置
            // people数组此时已经按照身高大到小排列,但站位不一定符合前k个人关系
            // 此时按照k插入:后边未排序的、k较小的数插入前面已经排好的数内,不会有任何新问题
            // 因为只要在该k之前的数都一定 >= 该数,不会使得已经排好的位置发生错误,
            // 在之前已排好高矮的前提下按k的大小插入新链表即可
        }

        return que.toArray(new int[people.length][]);
    }
}

划分字母区间

image.png

class Solution {
    public List<Integer> partitionLabels(String s) {
        int[] alph = new int[26]; // 对应26个字母的字典
        for (int i=0;i<s.length();i++){
            int j = s.charAt(i) - 'a';
            alph[j] = i; // 更新最后一次出现的位置
        }
        List<Integer> res = new ArrayList();
        int R = 0;
        int last = -1; // 左开右闭区间
        for (int i=0;i<s.length();i++){
            R = Math.max(R, alph[s.charAt(i) - 'a']);
            if (i == R){
                res.add(i-last);
                last = R;
            }
        }
        return res;
    }
}

738. 单调递增的数字

:::tips 题目描述:当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。 给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。 ::: 贪心思路
最大的数字 –> 相邻的两个数,前一位数减一, 此时比原先的数小的最大的数为 后一位数顶到 9 所以此时若为不单调的数字序列, 从_不单调的高位到低位满足:从不单调的数 -1,之后的数全变成9_

int n = 1234;
// 把整数拆分为-->单独的个位数的数字
String s = String.valueOf(n);
char[] chars = s.toCharArray();
// 把整数char集合转变为一个整数
Integer.parseInt(String.valueOf(chars));