贪心算法题目

贪心算法的题目主要是靠直觉和常识来形成做题思路

455. 分发饼干

class Solution {
    /**
    思路:将尽可能多的饼干分出去->每人最好吃得刚刚饱->从饥饿度最低的和最小的饼干开始匹配
     */
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);//将两个数组进行排序
        Arrays.sort(s);

        int i = 0;
        int j = 0;
        int count = 0;
        while(i < g.length && j < s.length){
            if(s[j] >= g[i]){//寻找最小的但能够符合最小饥饿值的饼干,若找到了,则将喂饱的人+1
                count++;
                i++;
            }
            j++;
        }

        return count;
    }
}

135. 分发糖果

class Solution {
    public int candy(int[] ratings) {
        /**
        1.初始化ans,使之全部为1
        2.从左到右看,如果ratings[right] > ratings[left],那么ans[right] = ans[left] + 1
        3.从右到左看,如果ratings[left] > ratings[right], and ans[left] <= ratings[right],then ans[left] = ans[right] + 1

        注意:顺序不能乱,不能是当左->右时,变化左边那个,因为这样就迭代不起来了
         */

         int[] ans = new int[ratings.length];
         for(int i = 0; i < ans.length; i++){//全部都分1颗糖果
             ans[i] = 1;
         }

         //从左往右,一个一个看
         for(int i = 0; i < ratings.length - 1; i++){//如果右边的分数比左边的高,那么右边的糖果要在左边的基础上+1(因为是要求至少,所以+1就可以了)
             if(ratings[i + 1] > ratings[i]){
                 ans[i + 1] = ans[i] + 1;
             }
         }

         //从右往左看
         for(int i = ratings.length - 1; i > 0; i--){//如果左边的分数比右边的高,但是左边得到的糖果不比右边的高,则左边得到的糖果变为右边糖果+1
             if(ratings[i - 1] > ratings[i] && ans[i - 1] <= ans[i]){
                 ans[i - 1] = ans[i] + 1;
             }

         }
         int sum = 0;
         for(int i = 0; i < ans.length; i++){//将记录下的糖果总总数加起来
             sum += ans[i];
         }
         return sum;

    }
}

435. 无重叠区间

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        /**
         找保留的最多区间数,也就是连接最紧密的所有区间
         1.先将所有的区间按右界排序
         2.取第一个区间的右界,看后面区间的左界是否在第一个区间内,如果在,则跳过这个区间,再判下一个区间,如果不在,那么最多区间数 + 1, 同时用于判断的右区间变为该区间的右区间,继续向下判断
         */

        Arrays.sort(intervals, new Comparator<int[]>() {//将所有元素,按区间右界进行从小到大排序
            @Override
            public int compare(int[] o1, int[] o2) {
                if(o1[1] > o2[1]){
                    return 1;
                }
                
                if(o1[1] < o2[1]){
                    return -1;
                }else{
                    return 0;
                }
                
            }
        });

        int count = 1;
        int rightBound = intervals[0][1];//定义第一个右界
        for(int i = 0; i < intervals.length; i++){拿右界一个个比元素的左界
            if(intervals[i][0] >= rightBound){如果发现有元素的左界超出了前一个元素的右界,那么说明这个区间并没有重合,则把新的右界赋值
                count++;
                rightBound = intervals[i][1];
            }
        }
        
        return intervals.length - count;
    }
}

121. 买卖股票的最佳时机

class Solution {
    public int maxProfit(int[] prices) {
        int maxProfit = 0;
        int min = Integer.MAX_VALUE;

        for(int i = 0; i <prices.length; i++){
            /**
            两个维度确定maxProfit,一个是减数,一个是被减数,只有两者之差最大时才是答案
            */

            //遍历数组,依次求每个卖出时机的的最大差值,再从中取最大值。
            min = Math.min(min, prices[i]);
            maxProfit = Math.max(maxProfit, prices[i] - min);
        }

        return maxProfit;
    }
}

122. 买卖股票的最佳时机 II

class Solution {
    public int maxProfit(int[] prices) {
        /**
        从头到尾遍历,如果发现prices[i + 1] - prices[i] > 0, then sum+=, i++
         */

        if(prices.length == 1){
            return 0;
        }
        int sum = 0;
         for(int i = 0; i < prices.length - 1; i++){
             if(prices[i + 1] - prices[i] > 0){
                 sum += prices[i + 1] - prices[i];
             }
         }
         return sum;

    }
}

406. 根据身高重建队列
 

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        /**
        思路:
        先按照身高进行排序,按身高从大到小进行排序,然后再看第二维,第二维是几,那就插入下标几,比如:
        如果第二维是2,那么表明该元素前面有两个比它高的元素,那么就插在第3这个位置(下标为2),
        */


        // 按照身高进行排序,如果身高相同的,再看第二维,第二维小的放在前面
        Arrays.sort(people, new Comparator<int[]>(){
            @Override
            public int compare(int[] o1, int[] o2){
                if(o1[0] > o2[0]){
                    return -1;
                }else if(o1[0] < o2[0]){
                    return 1;
                }else{
                    if(o1[1] > o2[1]){
                        return 1;
                    }else if(o1[1] < o2[1]){
                        return -1;
                    }else{
                        return 0;
                    }
                }
            }
        });    

        LinkedList<int[]> que = new LinkedList<>();

        for(int i = 0; i < people.length; i++){
            que.add(people[i][1], people[i]);
        }

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

}

像这种二维问题,必须用一维进行确定,然后再用第二维进行进一步的操作,不能两维同时操作(类似上面分糖果)

452. 用最少数量的箭引爆气球

class Solution {
    public int findMinArrowShots(int[][] points) {
        /**
        1.将points按照元素的右界进行排序,去一个元素的右界,判断剩下元素的左界是否在取出的右界之内,如果是,则判断下一个,如果不是,则跳到下一个元素的右界,同时count++;
         */
         int count = 1;
        Arrays.sort(points, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if(o1[1] > o2[1]){
                    return 1;
                }

                if(o1[1] < o2[1]){
                    return -1;
                }else{
                    return 0;
                }

            }
        });

        int rightBound = points[0][1];
        for(int i = 0;i < points.length; i++){
            if(points[i][0] > rightBound){如果有气球没有重合,那么需要多一把箭,count++,然后按新气球所在的范围再判定后面的气球
                rightBound = points[i][1];
                count++;
            }
        }

        return count;
    }
}

376. 摆动序列

class Solution {
    public int wiggleMaxLength(int[] nums) {
        /*
        思路:贪心算法,从左到右,把所有符合条件的,先全部记录下来再说
         */


        /**
        设置一个count,用来记录属于摆动序列的元素个数,从头到尾开始找。设置一个flag,用来判断下一个元素的差值需要的是正的还是负的,如果当前元素的差值为负数,那么flag=1, 正数则为-1,如果两个是相同的元素,那么flag = 0;
         */
        if(nums.length == 1){
            return 1;
        }

        if(nums.length == 2 && nums[0] == nums[1]){
            return 1;
        }
         int count = 1;
         if(nums[0] != nums[1]){
             count = 2;
         }

         int flag = -1 * (nums[1] - nums[0]);
        int res = 0;
        for(int i = 2; i < nums.length; i++){
            res = nums[i] - nums[i - 1];
            //res < 0 and flag <= 0
            //res > 0 and flag >= 0
            if(res < 0 && flag <= 0){
                count++;
                flag = -1 * res;
            }

            if(res > 0 && flag >= 0){
                count++;
                flag = -1 * (res); 
            }

        }
        return count;

    }
}
class Solution {
    public int wiggleMaxLength(int[] nums) {
        if(nums.length == 1){
            return 1;
        }

        int preDiff = 0;//前一个差值,如果前一个是谷底,则是>=0,前一个是顶峰,则是<=0
        int curDiff = 0;//当前一对差值
        int count = 1;//因为最右边必定会有一个峰值
        for(int i = 0; i < nums.length - 1; i++){
            curDiff = nums[i + 1] - nums[i];

            if((curDiff > 0 && preDiff <= 0) || (curDiff < 0 && preDiff >= 0)){//若pre是谷底,还没有走到顶峰,或若pre是顶峰,还没有走到谷底的时候,i就一直++,直到符合if这个条件
                count++;
                preDiff = curDiff;
            }
        }

        return count;
    }
}



53. 最大子序和

class Solution {
    public int maxSubArray(int[] nums) {
        /**
        从头开始加,如果加到发现tempSum是<0的,那么就将tempSum清零,从下一个元素重新加起,将最大的那个记录在maxSum里面
         */
         int maxSubArray = nums[0];
         int tempSum = 0;

         for(int i = 0; i < nums.length; i++){
             tempSum += nums[i];
             maxSubArray = Math.max(tempSum, maxSubArray);//必须在这里就开始调换,因为nums有可能全部都是负数,如果全是负数,
//则也是要在负数中找最大,而不是让后面被赋予0之后再找,因为0只是一个置零的效果,不是nums里面的元素

             if(tempSum < 0){
                 tempSum = 0;
             }
         }

         return maxSubArray;
    }
}

55. 跳跃游戏

class Solution {
    public boolean canJump(int[] nums) {
        if(nums.length == 1){
            return  true;
        }
        /**
         设定一个cover,第一个cover取第一个元素,此时cover的范围是[0, cover],在这个范围内找数组中的元素,看是否有i + nums[i] > cover的,
如果有,则跳到该元素这里,修改cover为i + nums[i],重复上面的判断。
每一次cover进行改变之后都要看看是否已经能覆盖到最后一个元素了,如果能,在返回true,如果经过了cover都不能,则返回false
         */

        int cover = nums[0];
        for(int i = 0; i <= cover; i++){//[i, cover]
            cover = Math.max(i + nums[i], cover);
            
            if(cover >= nums.length - 1){
                return true;
            }
        }
        return false;
    }
}

45. 跳跃游戏 II

class Solution {
    public int jump(int[] nums) {

    /**
    思路:到了一个元素之后,找这个元素cover的范围中,能够cover最多范围的元素,然后进行一次跳跃,得到新的范围,直到能够cover到最后的点
     */

        /**
         要选取最少,那么就是每次都要跳得最远

         设定一个cover,cover的最开始的值就是第一个元素所能包含的范围,在这个范围内,
         寻找能够跳的最远的元素,
         然后cover改成能跳得最远元素的值
         */
        if(nums.length == 1){
            return 0;
        }

        int i = 0;
        int count = 1;
        int cover = i + nums[i];
        while(true){
            if(cover >= nums.length - 1){
                return count;
            }else{
                int maxDistance = cover;
                int targetIndex = i;
                for(int j = i + 1; j <= i + nums[i]; j++){
                    if(j + nums[j] > maxDistance){
                        maxDistance = j + nums[j];
                        targetIndex = j;
                    }
                }
                cover = maxDistance;
                i = targetIndex;
                count++;
            }
        } 
    }
}
    public int jump(int[] nums) {
        if(nums.length == 1){
            return 0;
        }

        int curDistance = 0;//当前cover的范围
        int nextDistance = 0;//在cover范围中,某一元素能够cover的最大范围
        int count = 0;
        int i = 0;
        while(true){
            nextDistance = Math.max(nextDistance, i + nums[i]);//取cover中某一元素能够cover的最大范围

            if(nextDistance >= nums.length - 1){//如果知道了该元素能够cover的范围已经到结尾了
                count++;
                return count;
            }

            if(i == curDistance){//如果已经到了当前cover的最后,那么就要进行跳跃,
        //并且将当前的cover,
        //换为在遍历cover范围的过程中找到的新的最大的cover的范围
                curDistance = nextDistance;
                count++;
            }
            i++;
        }
    }

763. 划分字母区间

class Solution {
    public List<Integer> partitionLabels(String s) {
        /**
        思路:
        计算s中每一个字符出现的最后的位置,放置在edge中,然后从头开始查询字符,设定一个right值,
right值就是某一个字符出现的最后一个位置,在[i, right]的查询过程中,如果出现了更加大的right,那么
更新right,直到i == right,表明已经到了刚刚遍历过这串字串的最后一个字符
         */

        List<Integer> list = new LinkedList<>();
        int[] edge = new int[26];
        char[] chars = s.toCharArray();

        for(int i = 0; i < chars.length; i++){
            edge[chars[i] - 'a'] = i;//如果是同一个字符,那么chars[i] - 'a'就是相同的,那么该索引的值就能被更新
        }

        int right = 0;
        int previous = -1;//设定-1是因为,对于第一串字符串,长度是right - 0 + 1
        for(int i = 0; i < chars.length; i++){
            right = Math.max(right, edge[chars[i] - 'a']);//按字母排的,而不是按索引排的

            if(i == right){
                list.add(i - previous);
                previous = i;
            }
        }

        return list;

    }
}

56. 合并区间

class Solution {
    public int[][] merge(int[][] intervals) {
        /**
         思路:因为合并之后的元素个数可能会发生改变,所以采用ArrayList
         将区间按左边界进行排列,记录下每一次新元素的左边界start, 然后进行遍历,
         如果intervals[i][左边界] < intervals[i - 1][右边界],那么对intervals[i]进行扩大,
         扩大为intervals[i]和intervals[i - 1]中,有边界更大的那个,
         即intervals[i][1] = Math.max(intervals[i][1], intervals[i - 1][1])
         
         
         */

        List<int[]> res = new ArrayList<>();
        Arrays.sort(intervals, new Comparator<int[]>() {//按左边界进行排序,按右边界会出错
            @Override
            public int compare(int[] o1, int[] o2) {
                if(o1[0] > o2[0]){
                    return 1;
                }

                if(o1[0] < o2[0]){
                    return -1;
                }else{
                    return 0;
                }
            }
        });

        int start = intervals[0][0];
        for(int i = 1; i < intervals.length; i++){
            if(intervals[i][0] > intervals[i - 1][1]){
                res.add(new int[]{start, intervals[i -  1][1]});
                start = intervals[i][0];
            }else{
                intervals[i][1] = Math.max(intervals[i - 1][1], intervals[i][1]);//若发生重合了,则进行修改,将该区间不断地扩大,
                //使之产生一种递进的方式
            }
        }
        res.add(new int[]{start, intervals[intervals.length - 1][1]});//将最后一个元素所覆盖的范围放进去
        return res.toArray(new int[res.size()][]);
    }
}

738. 单调递增的数字

class Solution {
    public int monotoneIncreasingDigits(int n) {
        /**
         先将n拆分到一个nums数组里面

         从右往左看,如果nums[i] < nums[i - 1],说明不是递增的,则nums[i - 1]--, 然后i-1后面所有
         的元素,都设为9。不断地向前推进
         */

        int count = 0;
        int tempN = n;
        while(tempN != 0){
            int temp = tempN % 10;
            count++;
            tempN /= 10;
        }

        int[] nums = new int[count];
        for(int i = count - 1; i >= 0; i--){
            int temp = n % 10;
            nums[i] = temp;
            n /= 10;
        }

        for(int i = nums.length - 1; i > 0; i--){//注意要进行迭代,所以要从后面开始
            if(nums[i - 1] > nums[i]){
                nums[i - 1]--;
                for(int j = i; j < nums.length; j++){
                    nums[j] = 9;
                }
            }
        }

        int res = 0;
        for(int i = 0; i < nums.length; i++){
            res = res*10 + nums[i];
        }

        return res;
    }
}

从后往前时,注意是i--

714. 买卖股票的最佳时机含手续费

class Solution {
    public int maxProfit(int[] prices, int fee) {
        int maxProfit = 0;

        if(prices.length == 1){
            return 0;
        }

        int buy = fee + prices[0];
        for(int i = 1; i < prices.length; i++){
            if(prices[i] + fee < buy){
                buy = prices[i] + fee;
            }else if(prices[i] > buy){
                maxProfit += prices[i] - buy;
                buy = prices[i];//此时,需要一直持有这只股票,如果发现下一天的股票更加高,那么,
                //可以直接用这只股票的价格作为成本,用更高一支股票的价格来减,得到差价
            }

        }

        return maxProfit;
    }
}

待后续补充

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值