《趣学算法-贪心算法》阅读笔记

简介: 《趣学算法-贪心算法》阅读笔记

14天阅读挑战赛

在这里插入图片描述

1.概念

从贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优。所以自然而然,贪心算法得出的最后的解,不一定是问题的最优解,很有可能是近似解。

1.1 两个特性

算法的基本要素:

  • 最优子结构性质:

当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。

  • 贪心选择性质:

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

1.2 与暴力、动态规划的区别与联系

贪心算法最容易和暴力算法、动态规划相互混淆。
动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
动态规划算法,最基础的是要找到动态转移方程,然后使用递归或者栈的操作,去实现。
贪心算法,最基础的是最初要选择一种贪心策略(例如:每次选择最大的or最小的),然后一步一步的去达到目的。
暴力算法,是在找不到任何特征的前提下,遍历所有可能,去找寻答案的一种算法。

2.贪心的实际使用场景

2.1 冒泡排序

冒泡排序算法的实现思想是:
“每一趟 ” 都需要从第一位开始进行相邻的两个数的比较,将较大的数放后面,比较完毕之后向后挪一位继续比较下面两个相邻的两个数大小关系,重复此步骤,直到最后一个还没归位的数。
每一趟只能确定将一个数归位。即第一趟只能确定将末位上的数归位,第二趟只能将倒数第 2 位上的数归位,依次类推下去。如果有 n 个数进行排序,只需将 n-1 个数归位,也就是要进行 n-1 趟操作。
动画演示:
在这里插入图片描述
java代码实现

import java.util.Arrays;

class Main {
    public static void main(String[] args) {
        int[] a = new int[] { 2, 22, 1, 0, 99, 2, 5, 19, 32 };
        bubbleSort(a);
        System.out.println(Arrays.toString(a));
    }

    /**
     * 冒泡排序
     * 
     * @Title: bubbleSort
     * @Description:
     * @author: itbird
     * @date 2022年10月20日 上午10:39:28
     * @param a
     *            void 时间复杂度: O(N^2) 空间复杂度: O(1)
     */
    private static void bubbleSort(int[] a) {
        if (a == null || a.length <= 1) {
            return;
        }

        boolean swap = false;
        for (int i = 0; i < a.length; i++) {
            swap = false;
            // 通过每次遍历,将最大元素移动到数组末尾
            for (int j = 0; j < a.length - i - 1; j++) {
                if (a[j] > a[j + 1]) {
                    int temp = a[j];
                    a[j] = a[j + 1];
                    a[j + 1] = temp;
                    swap = true;
                }
            }

            // 剪枝,如果某一轮对比中,没有进行交换,说明当前数组已经有序
            if (!swap) {
                break;
            }
        }
    }

}

2.2 最优装载问题

问题描述:有一批集装箱要装上一艘载重量为C的轮船。其中集装箱i的重量为wi。最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。
解题思路:大家发现这时的问题相比于背包问题,有两点不同,一没有货物价值的考虑,二货物也不可分割,只需考虑装载的物品需要最多。那么此处其实我们采取贪心策略即可,即每次选择最小的重量的集装箱,直到达到轮船的最大承载力 。

  • 申明k,k代表当前轮船的剩余装载力还要多少,初始值为C
  • 先将w数组排序
  • 遍历w数组,然后从中取出货物,逐渐往船上装载,直到装载不上

Java代码实现:

class Main {
    public static void main(String[] args) {
        int c = 200; // 最大承载力为200
        int[] w = new int[] { 2, 22, 1, 0, 99, 2, 5, 19, 32 }; // 每个集装箱的重量大小

        // 先将w数组排序
        bubbleSort(w);

        int k = c; // k代表当前轮船的剩余装载力还要多少
        // 遍历w数组,然后从中取出货物,逐渐往船上装载,直到装载不上
        for (int i = 0; i < w.length; i++) {
            if (k > w[i]) {
                // 如果当前轮船还可以装载,则装载此集装箱w[i]
                k -= w[i];
            } else {
                break;
            }
        }

        System.out.println("轮船的最优装载量为:" + (c - k));
    }

    /**
     * 冒泡排序
     * 
     * @Title: bubbleSort
     * @Description:
     * @author: itbird
     * @date 2022年10月20日 上午10:39:28
     * @param a
     *            void 时间复杂度: O(N^2) 空间复杂度: O(1)
     */
    private static void bubbleSort(int[] a) {
        if (a == null || a.length <= 1) {
            return;
        }

        boolean swap = false;
        for (int i = 0; i < a.length; i++) {
            swap = false;
            // 通过每次遍历,将最大元素移动到数组末尾
            for (int j = 0; j < a.length - i - 1; j++) {
                if (a[j] > a[j + 1]) {
                    int temp = a[j];
                    a[j] = a[j + 1];
                    a[j + 1] = temp;
                    swap = true;
                }
            }

            // 剪枝,如果某一轮对比中,没有进行交换,说明当前数组已经有序
            if (!swap) {
                break;
            }
        }
    }

}

2.3 背包问题(货物可拆解)

问题描述:我们有一辆三轮车,某天发现了一个山洞,假设山洞中有n中宝物,每种宝物有一定的重量和相应的价值,而三轮车的运载能力有限,只能运走m重量的宝物,一种宝物只可以拿一样,宝物可以分割,那么怎么才可以一次拿走最大价值的宝物呢?。
解题思路:分析问题中,大家发现相比于0-1背包问题来说,此问题中的宝物可以分割。那么我们采取贪心策略,选择怎样的贪心策略呢?

  • 每次选择最轻的宝物?
  • 每次选择价值最大的宝物?
  • 每次选择单位价值最大的宝物?

思考一下,如果选价值最大的宝物,但重量非常大,也是不行的,因为运载能力是有限的,所以第 1 种策略舍弃;如果选重量最小的物品装入,那么其价值不一定高,所以不能在总重限制的情况下保证价值最大,第 2 种策略舍弃;而第 3 种是每次选取单位重量价值最大的宝物,也就是说每次选择性价比(价值/重量)最高的宝物,如果可以达到运载重量 m,那么一定能得到价值最大。
因此采用第 3 种贪心策略,每次从剩下的宝物中选择性价比最高的宝物。

Java代码实现:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class Main {
    public static void main(String[] args) {
        int m = 19; // 最大承载力为19
        double[] w = new double[] { 2, 6, 7, 4, 10, 3 }; // 每个宝物的重量大小
        double[] v = new double[] { 8, 1, 9, 3, 2, 4 }; // 每个宝物的价值大小

        List<Treasure> treaseures = new ArrayList<Treasure>();
        // 遍历w、v将输入数据,封装为宝物信息
        for (int i = 0; i < v.length; i++) {
            treaseures.add(new Treasure(w[i], v[i]));
        }

        // 对宝物信息封装,以单位价值进行排序
        Collections.sort(treaseures);

        int k = m; // k代表当前三轮车的剩余承载力
        double maxV = 0; // 最大价值
        for (int i = 0; i < treaseures.size(); i++) {
            if (k > treaseures.get(i).w) {
                k -= treaseures.get(i).w;
                maxV += treaseures.get(i).v;
            } else {
                maxV += k * treaseures.get(i).v / treaseures.get(i).w;
            }
        }
        System.out.println("所能一次装载的最大价值宝物为:" + maxV);
    }

    /**
     * 宝物的类
     * 
     * @author itbird
     *
     */
    static class Treasure implements Comparable<Treasure> {
        // 宝物重量
        double w;
        // 宝物价值
        double v;

        public Treasure(double w, double v) {
            this.w = w;
            this.v = v;
        }

        @Override
        public int compareTo(Treasure o) {
            return (int) (v / w - o.v / o.w);
        }
    }
}
目录
相关文章
|
2月前
|
算法 C++ Python
数据结构与算法===贪心算法
数据结构与算法===贪心算法
|
5天前
|
算法
【算法】贪心算法——柠檬水找零
【算法】贪心算法——柠檬水找零
|
5天前
|
算法
【算法】贪心算法简介
【算法】贪心算法简介
|
1月前
|
算法 Python
Python算法高手进阶指南:分治法、贪心算法、动态规划,掌握它们,算法难题迎刃而解!
【7月更文挑战第10天】探索Python算法的精华:分治法(如归并排序)、贪心策略(如找零钱问题)和动态规划(解复杂问题)。通过示例代码揭示它们如何优化问题解决,提升编程技能。掌握这些策略,攀登技术巅峰。
39 2
|
1月前
|
存储 算法 Python
Python算法界的秘密武器:分治法巧解难题,贪心算法快速决策,动态规划优化未来!
【7月更文挑战第9天】Python中的分治、贪心和动态规划是三大关键算法。分治法将大问题分解为小问题求解,如归并排序;贪心算法每步选局部最优解,不保证全局最优,如找零钱;动态规划存储子问题解求全局最优,如斐波那契数列。选择合适算法能提升编程效率。
41 1
|
1月前
|
存储 算法 大数据
Python算法高手的必修课:深入理解分治法、贪心算法、动态规划,让你的代码更智能!
【7月更文挑战第9天】在Python算法学习中,分治法(如归并排序)将大问题分解为小部分递归解决;贪心算法(如货币找零)在每步选择局部最优解尝试达到全局最优;动态规划(如斐波那契数列)通过存储子问题解避免重复计算,解决重叠子问题。掌握这三种方法能提升代码效率,解决复杂问题。
32 1
|
1月前
|
算法 索引 Python
逆袭算法界!Python分治法、贪心算法、动态规划深度剖析,带你走出算法迷宫!
【7月更文挑战第8天】分治法,如快速排序,将大问题分解并合并解;贪心算法,选择局部最优解,如活动选择;动态规划,利用最优子结构避免重复计算,如斐波那契数列。Python示例展示这些算法如何解决实际问题,助你精通算法,勇闯迷宫。
23 1
|
1月前
|
算法 索引 Python
Python算法设计与分析大揭秘:分治法、贪心算法、动态规划...掌握它们,让你的编程之路更加顺畅!
【7月更文挑战第8天】探索Python中的三大算法:分治(如快速排序)、贪心(活动选择)和动态规划(0-1背包问题)。分治法将问题分解求解再合并;贪心策略逐步求局部最优;动态规划通过记忆子问题解避免重复计算。掌握这些算法,提升编程效率与解决问题能力。
27 1
|
2月前
|
存储 算法 Java
技术笔记:JVM的垃圾回收机制总结(垃圾收集、回收算法、垃圾回收器)
技术笔记:JVM的垃圾回收机制总结(垃圾收集、回收算法、垃圾回收器)
30 1
|
1月前
|
算法 开发者 Python
惊!Python算法界的三大神器:分治法、贪心算法、动态规划,让你秒变算法大师!
【7月更文挑战第8天】在Python编程中,分治、贪心和动态规划是核心算法。分治如归并排序,将大问题拆解并递归求解;贪心算法针对找零问题,每次都选最大面额硬币,追求局部最优;动态规划则通过记忆化避免重复计算,如斐波那契数列。这些算法巧妙地提升效率,解决复杂问题。
19 0

热门文章

最新文章