数据结构与算法(三)贪心算法(Java)

目录

    • 一、简介
      • 1.1 定义
      • 1.2 基本步骤
      • 1.3 优缺点
    • 二、经典示例
      • 2.1 选择排序
      • 2.2 背包问题
    • 三、经典反例:找零钱
      • 3.1 题目
      • 3.2 解答
      • 3.3 记忆化搜索实现
      • 3.4 动态规划实现

一、简介

1.1 定义

贪心算法(Greedy Algorithm),又名贪婪法,是寻找 最优解问题 的常用方法。

  • 将求解过程 分成若干个步骤,每个步骤都应用贪心原则,选取当前状况下最好/最有的选择(局部最有利的选择),并以此希望最后堆叠出的结果也是最好/最优的解。

1.2 基本步骤

  • 步骤1:从某个初始解出发;
  • 步骤2:把求解的问题分成若干个子问题;
  • 步骤3:对每一子问题求解,得到子问题的局部最优解;
  • 步骤4:把子问题的局部最优解合成原来问题的一个解。

1.3 优缺点

优点:

  • 简单、高效,省去为了寻找最优解可能需要穷举的操作,通常作为其他算法的辅助算法来使用

缺点:

  • 不从整体上考虑其它可能情况,每次选取局部最优解,不再进行回溯处理,所以 并非一定能得到整体最优解

二、经典示例

2.1 选择排序

没错,我们常见的选择排序就是运用了贪心算法的思想。

题目:

  • 实现数字数组递增排序。

解答:

从数组的零下标开始,依次从后面找到最小的元素下标与当前位置的元素互换,这个在后面寻找最小元素的过程就是贪心的思想。

贪心策略寻找最小的元素,(贪心地)认定此元素就是当前位置的最小元素,然后遍历每一个位置。

public void choiceSort(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        int minIndex = i;
        for (int j = i + 1; j < arr.length; j++) {
            minIndex = arr[j] < arr[minIndex] ? j : minIndex;
        }
        if (minIndex != i) {
            int tmp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = tmp;
        }
    }
}

2.2 背包问题

题目:

有一个背包,容量由你自己输入,有n个物品,每个物品都具有容量与价值,这些都是由你自己输入的,请问,要怎么放物品到背包里,才能使得总价值最大呢,放入背包的总容量要小于等于背包的总容量。(如果一个物品放不下,则可以拆分成多个小块)
背包:M:100
物品:N:7
重量 价值
10 20
20 40
30 30
25 20
50 40
10 35
60 70

解答:

每个物品都具有自己的重量与价格,不妨计算出每个物品的单位价值。

  • 单位价值: 价值/重量,即每份重量的价值。

然后我们将这些物品 按照单位价值递减排序。这样一来就简单了,只需用贪心算法,依次把最大单位价值的物品价值和重量相加 就行了。

贪心策略:单位价值最大的物品,我们假设它就是最好的,直接把它放在背包里面。

public static void main(String[] args) {
    int[][] items = new int[7][2];
    items[0][0] = 10; items[0][1] = 20;
    items[1][0] = 20; items[1][1] = 40;
    items[2][0] = 30; items[2][1] = 30;
    items[3][0] = 25; items[3][1] = 20;
    items[4][0] = 50; items[4][1] = 40;
    items[5][0] = 10; items[5][1] = 35;
    items[6][0] = 60; items[6][1] = 70;
    int capacity = 100;
    System.out.println("背包的容量:" + capacity);
    StringBuilder builder = new StringBuilder();
    for (int[] item : items) {
        builder.append(Arrays.toString(item));
    }
    System.out.println(items.length + " 个物品的重量、价值:" + builder.toString());
    int maxValue = maxValue(items, capacity);
    System.out.println("最大价值:" + maxValue);
}

public static int maxValue(int[][] items, int capacity) {
    // 计算单位价值
    double[] prices = new double[items.length];
    Map<Double, List<Integer>> positionMap = new HashMap<>(items.length);
    for (int i = 0; i < items.length; i++) {
        prices[i] = 1.0 * items[i][1] / items[i][0];
        List<Integer> positions = positionMap.getOrDefault(prices[i], new ArrayList<>());
        positions.add(i);
        positionMap.put(prices[i], positions);
    }
    // 排序
    Arrays.sort(prices);
    int weight = 0;
    int maxValue = 0;
    for (int i = prices.length - 1; i >= 0; i--) {
        List<Integer> positions = positionMap.get(prices[i]);
        if (positions != null) {
            Integer position = positions.remove(0);
            if (positions.size() == 0) {
                positionMap.remove(prices[i]);
            }
            if (weight + items[position][1] < capacity) {
                weight += items[position][0];
                maxValue += items[position][1];
                System.out.println("重量为 " + items[position][0] + ",价值为 " + items[position][1] + " 的物品被放入背包,剩余容量:" + (capacity - weight));
            }
        }
    }
    return maxValue;
}

执行结果:

在这里插入图片描述


三、经典反例:找零钱

322. 零钱兑换

3.1 题目

假设你开了间小店,不能电子支付,钱柜里的货币只有 25 分、20分、10 分、5 分和 1 分 四种硬币,如果你是售货员且要找给客户 41 分钱的硬币,如何安排才能找给客人的钱既 正确 且硬币的个数又 最少

3.2 解答

我们看到这种题目可能第一个想法就是用 贪心算法 进行解决,其实不然,由于贪心算法不能进行回溯处理,所以并不能取得最优解。

  • 41 分钱按照贪心算法,先找 25分,剩余 16分,再找 10分、5分、1分,共需要 4 枚硬币。
  • 实际情况下,我们只需要找两个 20分钱,再找一个 1分钱就够了,共需要 3 枚硬币。

那么不用贪心算法,应该用什么算法呢?其实有两种方法可以解决:

  • 一是 贪心+回溯,即 记忆化搜索
  • 二是 动态规划

3.3 记忆化搜索实现

  • 记忆化搜索实现方式,就是自顶向下遍历,先查用完一枚硬币之后还剩多少钱,再根据剩余的钱进行迭代。

代码实现需要注意以下几点:

  • int[] 类型数组初始化值为0;
  • 针对需要记忆的组合,不光要记忆成功的情况,失败的情况也要记录。
class Solution {
	public static void main(String[] args) {
        int amount = 41;
        int[] arr1 = {1, 5, 10, 20, 25};
        System.out.println("零钱总数为:" + amount);
        System.out.println("硬币面值为:" + Arrays.toString(arr1));
        int result1 = coinChange(arr1, amount);
        System.out.println("最少使用硬币数:" + result1);
    }

    public static int coinChange(int[] coins, int amount) {
        if (amount == 0) {
            return 0;
        }
        return handleCoin(coins, new int[amount], amount);
    }

    private static int handleCoin(int[] coins, int[] his, int coinAmount) {
        if (coinAmount < 0) {
            return -1;
        }
        if (coinAmount == 0) {
            return 0;
        }
        if (his[coinAmount - 1] != 0) {
            return his[coinAmount - 1];
        }
        int minCount = Integer.MAX_VALUE;
        for (int coin : coins) {
            int tmpMinCount = handleCoin(coins, his, coinAmount - coin);
            if (tmpMinCount != -1 && tmpMinCount + 1 < minCount) {
                minCount = tmpMinCount + 1;
            }
        }
        his[coinAmount - 1] = minCount == Integer.MAX_VALUE ? -1 : minCount;
        return his[coinAmount - 1];
    }
}

执行结果:

在这里插入图片描述

3.4 动态规划实现

  • 动态规划实现,则是自底向上,先计算从1开始每个金额所需的最小零钱数,直到找到需要的钱数,过程中对于剩余钱数所需要的最小零钱数可以直接使用前面计算好的数据。

代码实现需要注意以下几点:

  • 对于数值类型的映射,不要用 Map 类型,用 int[] 类型效率更高;
  • 在循环迭代的过程中,只需要加硬币数就可以了,不用再去迭代考虑其他的组合。
class Solution {
    public static void main(String[] args) {
        int amount = 41;
        int[] arr1 = {1, 5, 10, 20, 25};
        System.out.println("零钱总数为:" + amount);
        System.out.println("硬币面值为:" + Arrays.toString(arr1));
        int result1 = coinChange(arr1, amount);
        System.out.println("最少使用硬币数:" + result1);
    }

    public static int coinChange(int[] coins, int amount) {
        // k-零钱和,v-最小零钱量
        int[] his = new int[amount + 1];
        Arrays.fill(his, amount + 1);
        his[0] = 0;
        for (int i = 1; i <= amount; i++) {
            for (int coin : coins) {
                if (coin <= i) {
                    his[i] = Math.min(his[i], his[i - coin] + 1);
                }
            }
        }
        return his[amount] == amount + 1 ? -1 : his[amount];
    }
}

执行结果:

在这里插入图片描述

整理完毕,完结撒花~ 🌻





参考地址:

1.小白带你学—贪心算法(Greedy Algorithm),https://zhuanlan.zhihu.com/p/53334049

2.贪心算法思想详解+示例代码,https://blog.csdn.net/jj6666djdbbd/article/details/126971331

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/187890.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CSS新特性(2-2)

CSS新特性&#xff08;2-2&#xff09; 前言box相关box-shadow background背景rgba颜色与透明度transform:rotate(Xdeg) 2D旋转transform:tranlate 平移 前言 本文继续讲解CSS3其他的新特性&#xff0c;想看之前新特性点击这里&#xff0c;那么好本文正式开始。 box相关 box…

[element-ui] el-dialog 中的内容没有预先加载,因此无法获得内部元素的ref 的解决方案

问题描述 在没有进行任何操作的时候&#xff0c;使用 this.$refs.xxxx 无法获取el-dialog中的内部元素&#xff0c;这个问题会导致很多bug. 官方解释&#xff0c;在open事件回调中进行&#xff0c;但是open()是弹窗打开时候的会调&#xff0c;有可能在此处获取的时候&#xff…

[多线程】线程安全问题

目录 1.举个栗子 2.线程安全的概念 3.线程不安全的原因 3.1原子性 3.2Java内存模型&#xff08;jvm&#xff09; 3.3代码重排序 4.解决线程的不安全问题-&#xff08;synchronized&#xff09; ​编辑 4.1sychronized的特性 4.2刷新内存 4.3可重入 5.synchornized使…

「Verilog学习笔记」数据累加输出

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 在data_out准备好&#xff0c;valid_b拉高时&#xff0c;如果下游的ready_b为低&#xff0c;表示下游此时不能接收本模块的数据&#xff0c;那么&#xff0c;将会拉低ready…

JMeter 测试脚本编写技巧

JMeter 是一款开源软件&#xff0c;用于进行负载测试、性能测试及功能测试。测试人员可以使用 JMeter 编写测试脚本&#xff0c;模拟多种不同的负载情况&#xff0c;从而评估系统的性能和稳定性。以下是编写 JMeter 测试脚本的步骤。 第 1 步&#xff1a;创建测试计划 在JMet…

【Python百宝箱】Python轻松操控邮件:SMTP、POP3和IMAP的魅力

前言 在数字时代&#xff0c;电子邮件作为信息传递的主要手段&#xff0c;对个人和企业的日常工作至关重要。Python提供了多个强大的库&#xff0c;使得电子邮件的发送和接收变得轻松而灵活。本文将深入介绍Python中与电子邮件相关的主要库&#xff0c;为读者提供从基础到高级…

每日一题--删除链表的倒数第 N 个结点

破阵子-晏殊 燕子欲归时节&#xff0c;高楼昨夜西风。 求得人间成小会&#xff0c;试把金尊傍菊丛。歌长粉面红。 斜日更穿帘幕&#xff0c;微凉渐入梧桐。 多少襟情言不尽&#xff0c;写向蛮笺曲调中。此情千万重。 目录 题目描述&#xff1a; 思路分析&#xff1a; 方法及…

全面(16万字)深入探索深度学习:基础原理到经典模型网络的全面解析

前言 Stacking(堆叠) 网页调试 学习率&#xff1a;它决定了模型在每一次迭代中更新参数的幅度激活函数-更加详细 激活函数的意义: 激活函数主要是让模型具有非线性数据拟合的能力&#xff0c;也就是能够对非线性数据进行分割/建模 如果没有激活函数&#xff1a; 第一个隐层: l…

关于python中的nonlocal关键字

如果在函数的子函数中需要调用外部变量&#xff0c;一般会看见一个nonlocal声明&#xff0c;类似下面这种&#xff1a; def outer_function():x 10def inner_function():nonlocal xx 1print(x)inner_function()outer_function()在这个例子中&#xff0c;inner_function 引用…

[HCIE] IPSec-VPN (IKE自动模式)

概念&#xff1a; IKE&#xff1a;因特网密钥交换 实验目标&#xff1a;pc1与pc2互通 步骤1&#xff1a;R1与R3配置默认路由 R1&#xff1a; ip route-static 0.0.0.0 0.0.0.0 12.1.1.2 R2&#xff1a; ip route-static 0.0.0.0 0.0.0.0 23.1.1.2 步骤2&#xff1a;配ACL…

Java数组的复制、截取(内含例题:力扣-189.轮转数组)

目录 数组的复制、截取&#xff1a; 1、使用Arrays中的copyOf方法完成数组的拷贝 2、使用Arrays中的copyofRange方法完成数组的拷贝 题目链接&#xff1a; 数组的复制、截取&#xff1a; 1、使用Arrays中的copyOf方法完成数组的拷贝 public class Csdn {public static vo…

vscode的下载安装与配置【超详细】

1、下载 进入vscode官网 打开浏览器的下载内容管理&#xff0c;找到vscode下载任务&#xff0c;鼠标放在下载链接上并右击&#xff0c;点击复制链接地址 下载太慢&#xff1f;使用国内镜像 打开新窗口粘贴地址&#xff0c;并将域名改为&#xff1a;vscode.cdn.azure.cn&am…

ZKP11.4 Use CI to instantiate Fiat-Shamir

ZKP学习笔记 ZK-Learning MOOC课程笔记 Lecture 11: From Practice to Theory (Guest Lecturer: Alex Lombardi) 11.4 Use CI to instantiate Fiat-Shamir Avoid Bad Challenges Def: Given false claim x x x and a first message α \alpha α, a challenge β \beta …

JAVA小游戏“简易版王者荣耀”

第一步是创建项目 项目名自拟 第二部创建个包名 来规范class 然后是创建类 GameFrame 运行类 package com.sxt;import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; im…

java集合,ArrayList、LinkedList和Vector,多线程场景下如何使用 ArrayList

文章目录 Java集合1.2 流程图关系1.3 底层实现1.4 集合与数组的区别1.4.1 元素类型1.4.2 元素个数 1.5 集合的好处1.6 List集合我们以ArrayList集合为例1.7 迭代器的常用方法1.8 ArrayList、LinkedList和Vector的区别1.8.1 说出ArrayList,Vector, LinkedList的存储性能和特性1.…

【室内定位系统源码】UWB超宽带定位技术的特点和应用前景

uwb人员、物品定位系统源码&#xff0c;智慧工厂人员安全管理定位&#xff0c;高精度定位系统源码 UWB超宽带定位技术概念&#xff1a; 超宽带无线通信技术&#xff08;UWB&#xff09;是一种无载波通信技术&#xff0c;UWB不使用载波&#xff0c;而是使用短的能量脉冲序…

解决PDF预览时,电子签章、日期等不显示问题

文章目录 问题描述问题排查问题解决 问题描述 在预览PDF时&#xff0c;部分签章或控件没有显示。如下图&#xff1a; 正确应该要这样&#xff1a; 问题排查 根据网上搜索&#xff0c;排查&#xff0c;我先看看&#xff0c;pdf.worker.js 里的这三行代码&#xff0c;是否已经注…

无需API开发,有赞小程序集成广告推广系统,提升品牌曝光

无需API开发&#xff0c;实现有赞小程序与其他系统的连接 有赞小程序作为一个多功能的电子商务解决方案&#xff0c;为商家提供了无需复杂API开发就可以实现系统连接和集成的便捷途径。通过有赞小程序&#xff0c;商家可以轻松实现与各种系统的数据同步和应用互联&#xff0c;…

【机器学习】聚类(二):原型聚类:LVQ聚类(学习向量量化)

文章目录 一、实验介绍1. 算法流程2. 算法解释3. 算法特点4. 应用场景5. 注意事项 二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 导入必要的库1. LVQ类a. 构造函数b. 闵可夫斯基距离c. LVQ聚类过程e. 聚类结果可视化 2. 辅助函数3. 主函数a. 命令行界面 &#xff…

【MATLAB】VMD分解+FFT+HHT组合算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 VMD&#xff08;Variational Mode Decomposition&#xff09;是一种信号分解方法&#xff0c;基于HHT&#xff08;Hilbert-Huang Transform&#xff0c;希尔伯特-黄变换&#xff09;。HH…