巧用贪心算法

简介: 五大常用算法(分治、动态规划、贪心、回溯、分支界限(深广优先遍历)),我们之前的文章基本上都有涵盖,唯独差一个贪心算法,本篇文章我们将一起走进贪心算法的妙用。

基本概念

贪心算法(又称贪婪算法),是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解

贪心算法不是对所有问题都能得到整体最优解,但许多问题他能产生整体最优解或者是整体最优解的近似解。

题目特点

我们如何能够确定一道题是否可以用贪心算法求解呢?

因为贪心算法求得的结果不一定是整体最优解,所以我们很难断定它就一定可以用贪心算法。

可以用贪心算法的题目一般具有以下特性

  • 贪心选择性质

    贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。

    动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。

    对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。

  • 最优子结构性质

    当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。

    问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。

贪心算法和动态规划如何选择?

以背包问题为例,给定我们一个有容量限制的背包,和若干不同价值,不同重量的物品,如何选择可以使我们背包内物品价值最大?

这种问题在游戏场景里极为常见,比如自动拾取装备功能,肯定为玩家选择价值最大的装备拾取。

这类问题一般还可以细分为两种类型:

  • 可以拾取物品的一部分 (可以使用贪心,优先拾取性价比高的装备)
  • 物品不可分割,只能拾取,或者不拾取(不可以使用贪心,这类问题也叫0-1背包问题)

image.png

求解思路

一般可以使用贪心算法的题,也可以使用动态规划去做,但是考虑到动态规划的特点是求整体的最优解,在大部分题中贪心要比动态规划时空复杂度更低,也更容易实现。

无论哪些题,贪心算法最重要的是寻找子问题最具性价比的解,也就是局部最优解。

求解过程:

  • 把求解的问题分成若干个子问题。
  • 对每一子问题求解,得到子问题的局部最优解。
  • 把子问题的解局部最优解,合成原来解问题的一个解。

举例:上图背包问题,就可以通过贪心算法来解

步骤:

  • 子问题:装入某件物品
  • 子问题局部最优解:装入性价比最高的物品
  • 合并(所有装入物品价值和)
package com.zhj.interview;
​
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
​
public class Test10 {
    public static void main(String[] args) {
        int capacity = 15;
        int[] weights = {1,1,2,4,12};
        int[] values = {1,2,2,10,4};
        System.out.println("背包最大价值:" + getHighestValue(capacity, weights, values));
    }
​
    public static double getHighestValue(int capacity, int[] weights,int[] values){
​
        //创建物品列表并按照性价比倒序
        List<Item> itemList = new ArrayList<>();
        for(int i=0;i<weights.length;i++){
            itemList.add(new Item(weights[i], values[i]));
        }
        itemList = itemList.stream().sorted(Comparator.comparing(Item::getRatio).reversed()).collect(Collectors.toList());
​
        //背包剩余容量
        int restCapacity = capacity;
        //当前背包物品的最大价值
        double highestValue = 0;
​
        //按照性价比从高到低选择物品
        for(Item item : itemList){
            if(item.weight <= restCapacity){
                highestValue += item.value;
                restCapacity -= item.weight;
            }else{
                //背包装不下完整物品时,选择该件物品的一部分
                highestValue += (double)restCapacity/(double)item.weight * item.value;
                break;
            }
        }
​
        return highestValue;
    }
​
    static class Item {
        private int weight;
        private int value;
        //物品的性价比
        private double ratio;
​
        public Item (int weight, int value){
            this.weight = weight;
            this.value = value;
            this.ratio = (double)value / (double)weight;
        }
​
        public double getRatio() {
            return ratio;
        }
    }
}
目录
相关文章
|
6月前
|
算法 测试技术 索引
动态规划算法练习题
动态规划算法练习题
42 0
|
移动开发 算法 调度
【贪心算法】一文让你学会“贪心”(贪心算法详解及经典案例)
贪心算法是一种非常常见的算法,它的简单和高效性使其在实际应用中被广泛使用。 贪心算法的核心思想是在每一步都采取当前状态下最优的选择,而不考虑未来可能产生的影响。虽然贪心算法不能保证总是得到最优解,但在很多情况下,它可以获得很好的结果。 本篇文章将介绍贪心算法的基本概念和一些经典应用,以及如何通过贪心算法来解决一些实际问题。希望通过本文的阅读,读者可以对贪心算法有更加深刻的理解,并能够在实际问题中应用贪心算法来得到更好的解决方案。 让我们暴打贪心算法吧!
3341 0
|
1月前
|
存储 算法
动态规划算法学习一:DP的重要知识点、矩阵连乘算法
这篇文章是关于动态规划算法中矩阵连乘问题的详解,包括问题描述、最优子结构、重叠子问题、递归方法、备忘录方法和动态规划算法设计的步骤。
101 0
|
2月前
|
算法 前端开发
一文了解贪心算法和回溯算法在前端中的应用
该文章深入讲解了贪心算法与回溯算法的原理及其在前端开发中的具体应用,并通过分析LeetCode题目来展示这两种算法的解题思路与实现方法。
|
6月前
leetcode代码记录(动态规划基础题(斐波那契数列)
leetcode代码记录(动态规划基础题(斐波那契数列)
32 0
|
6月前
|
算法
贪心算法练习题
贪心算法练习题
39 0
|
算法 调度
算法基础课第八章动态规划和贪心算法
算法基础课第八章动态规划和贪心算法
112 0
算法基础课第八章动态规划和贪心算法
|
算法
【每日挠头算法题】Acwing 756. 蛇形矩阵 —— 巧妙解法
【每日挠头算法题】Acwing 756. 蛇形矩阵 —— 巧妙解法
137 0
【每日挠头算法题】Acwing 756. 蛇形矩阵 —— 巧妙解法
|
算法 搜索推荐
算法笔记(二)——快排,归并算法(做成模板题)
算法笔记(二)——快排,归并算法(做成模板题)
算法笔记(二)——快排,归并算法(做成模板题)
|
算法 测试技术
动态规划真的有那么抽象吗?(递推算法还是动态规划别傻傻分不清了) 以正确的姿势学习动态规划 (入门篇)
动态规划真的有那么抽象吗?(递推算法还是动态规划别傻傻分不清了) 以正确的姿势学习动态规划 (入门篇)
动态规划真的有那么抽象吗?(递推算法还是动态规划别傻傻分不清了) 以正确的姿势学习动态规划 (入门篇)