LeetCode--贪心算法概念总结和经典题目解析汇总

目录

贪心算法简介:

题目一:避免重复字母的最小删除成本

题目二:买股票的最佳时机2

题目三:分发饼干

题目四:跳跃游戏

题目五:用最少数量的箭引爆气球

题目六:数组大小减半


贪心算法简介:

是一种在每一步选择中都采取当前状态下最好或者最优(最有利)的选择,从而希望导致结果是全局最好/优的算法

贪心算法与动态规划的不同在于它对每个子问题的解决方案都作出选择,不能回退,

动态规划则会保存以前的运算结果,并根据当前进行选择,有回退功能。

题目一:避免重复字母的最小删除成本

(题目链接:https://leetcode-cn.com/problems/minimum-deletion-cost-to-avoid-repeating-letters/

给你一个字符串 s 和一个整数数组 cost ,其中 cost[i] 是从 s 中删除字符 i 的代价。

返回使字符串任意相邻两个字母不相同的最小删除成本。

请注意,删除一个字符后,删除其他字符的成本不会改变。

示例 1:

输入:s = "abaac", cost = [1,2,3,4,5]
输出:3
解释:删除字母 "a" 的成本为 3,然后得到 "abac"(字符串中相邻两个字母不相同)。
示例 2:

输入:s = "abc", cost = [1,2,3]
输出:0
解释:无需删除任何字母,因为字符串中不存在相邻两个字母相同的情况。
示例 3:

输入:s = "aabaa", cost = [1,2,3,4,1]
输出:2
解释:删除第一个和最后一个字母,得到字符串 ("aba") 。

提示:

s.length == cost.length
1 <= s.length, cost.length <= 10^5
1 <= cost[i] <= 10^4
s 中只含有小写英文字母

思路:贪心的思想,我们找到重复的字符字串,

并且保留字串中删除成本最高的字符,这样各处贪心会导致结果最优

Java源代码:

public class delete_cost {
    public static void main(String[] args) {
        //String s = "abaaac";
        String s = "aabaa";
        //int[] cost = new int[]{1,2,3,9,4,5};
        int[] cost = new int[]{1,2,3,4,1};
        int costsum = 0;
        int i = 0;
        int second_j =0;
        int length = 0;
        while(i<s.length()-1){
            int flag = 0;
            int cost_max =0;
            length = 0;
            for (int j = i+1; j <s.length() ; j++) {
                if(s.charAt(i) == s.charAt(j)){
                    length = j-i;
                    second_j = j;
                    flag =1;
                    continue;
                }else{
                    break;
                }
            }
            if(flag ==1){
                for(int m = i; m<=second_j;m++){
                    costsum += cost[m];
                    if(cost_max <cost[m]){
                        cost_max = cost[m];
                    }
                }
                costsum = costsum - cost_max;
                i= second_j;
            }else{
                i = i+1; //有的话加length否则加1
            }

        }
        System.out.println("costsum: "+costsum);
    }
}

题目二:买股票的最佳时机2

(题目链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 

思路:贪心的思想,遍历整个数组,只要发现有后一天比今天的价格高,表明我们今天可以买入股票,而后天可以卖出股票

所以从前往后遍历,发现后一天比今天高的我们就记录下来,并计算出高出多少。

class Solution {
    public int maxProfit(int[] prices) {
        int res = 0;
        for(int i =0;i <prices.length-1;i++){
            if(prices[i] < prices[i+1]){
                res = res+prices[i+1]-prices[i];
            }
        }
        return res;
    }
}

题目三:分发饼干

(题目链接:https://leetcode-cn.com/problems/assign-cookies/

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

对每个孩子 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。

思路:先对两个数组进行从小到大排序,接着采用贪心的思想,对于每一个孩子,

每次尽可能找到饼干数组里面最小的且满足孩子胃口的饼干。

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        int res = 0;
        int glength = g.length;
        int slength = s.length;
        Arrays.sort(g);
        Arrays.sort(s);
        int i=0;
        int j=0;
        while (i<glength && j<slength){
            if(s[j] >=g[i]){
                res++;
                i++;
                j++;
            }else{
                j++;
            }
        }
        return res;
    }
}

题目四:跳跃游戏

(题目链接: https://leetcode-cn.com/problems/jump-game/

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。

思路:从前往后跳跃,只要当当前位置小于可以继续跳的最远位置成立的时候,我们才继续跳跃

而最远跳跃怎么求?之前的最远位置和当前位置能够去的最远位置中去较大值。

class Solution {
    public boolean canJump(int[] nums) {
        if(nums.length ==0){
            return false;
        }
        int rightmost = 0;
        for(int i =0;i<nums.length;i++){
            if(i<=rightmost){
                rightmost = Math.max(rightmost, i+nums[i]);
                if(rightmost >= nums.length-1){
                    return true;
                }
            }
        }
        return false;
    }
}

题目五:用最少数量的箭引爆气球

(题目链接:https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/

在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,

气球直径的开始和结束坐标。由于它是水平的,所以纵坐标并不重要,

因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。

一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,

若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足  xstart ≤ x ≤ xend,

则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,

可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

给你一个数组 points ,其中 points [i] = [xstart,xend] ,返回引爆所有气球所必须射出的最小弓箭数。
示例 1:

输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:对于该样例,x = 6 可以射爆 [2,8],[1,6] 两个气球,以及 x = 11 射爆另外两个气球

官方解析:

简洁的说就是按照右边界大小进行排序,然后每只箭尽可能往右边靠,但是要保持原来可以引爆气球仍然可以引爆。

class Solution {
    public int findMinArrowShots(int[][] points) {
        if(points == null || points.length == 0){
            return 0;
        }
        Arrays.sort(points,(a,b)->a[1]>b[1]?1:-1);
        int last = points[0][1];
        int count = 1;
        for(int i =0;i<points.length;i++){
            if(last<points[i][0]){
                count++;
                last = points[i][1];
            }
        }
        return count;
    }
}

【注意】:对于类似二维数组的排序我们采用的是:

 Arrays.sort(points, (a,b)->a[1]>b[1] ?1 :-1)

题目六:数组大小减半

(题目链接:https://leetcode-cn.com/problems/reduce-array-size-to-the-half/

给你一个整数数组 arr。你可以从中选出一个整数集合,并删除这些整数在数组中的每次出现。

返回 至少 能删除数组中的一半整数的整数集合的最小大小。

示例 1:

输入:arr = [3,3,3,3,5,5,5,2,2,7]
输出:2
解释:选择 {3,7} 使得结果数组为 [5,5,5,2,2]、长度为 5(原数组长度的一半)。
大小为 2 的可行集合有 {3,5},{3,2},{5,2}。
选择 {2,7} 是不可行的,它的结果数组为 [3,3,3,3,5,5,5],新数组长度大于原数组的二分之一。

思路:直接暴力法,用hash表存储每个元素的出现次数,把次数放到一个数组中,

对数组进行排序,最后遍历数组,求出满足条件的结果

class Solution {
    public int minSetSize(int[] arr) {
        Map<Integer,Integer> map = new HashMap<Integer,Integer>();
        int[] lengtharr = new int[arr.length];
        int minlength = arr.length/2;
        for(int i =0;i<arr.length;i++){
            if(map.containsKey(arr[i])){
                int tempcount = map.get(arr[i]);
                map.put(arr[i],tempcount+1);
            }else{
                map.put(arr[i],1);
            }
        }
        int k=0;
        int reallength = 0;
        int count = 0;
        for(int key:map.keySet()){
            lengtharr[k++] = map.get(key);
        }
        Arrays.sort(lengtharr);
        for(int i = lengtharr.length - 1 ; i>=0; i--){
            count++;
            reallength = reallength+lengtharr[i];
            if(reallength>=minlength){
                return count;
            }
        }
        return 0;
    }
}

猜你喜欢

转载自blog.csdn.net/yezonghui/article/details/112055512