Java 数据结构与算法系列精讲之贪心算法

Java 数据结构与算法系列精讲之贪心算法

什么是贪心算法?

在计算机科学中,贪心算法是一种通过选择局部最优解来实现全局最优解的优化算法。贪心算法在解决某些最优化问题时非常有效,贪心算法能够达到接近最优解,有时甚至能够达到最优解。

贪心算法解题步骤:

  1. 建立算法模型
  2. 找出最优解的子结构
  3. 设计贪心选择策略
  4. 实现贪心选择策略为一个最优解
  5. 证明贪心算法的正确性

贪心算法应用场景

贪心算法应用场景非常广泛,常见的有:

  • 任务调度问题
  • 找零钱问题
  • 最短路径问题
  • Huffman编码问题

任务调度问题

一般而言,在任务调度问题中,我们将调度任务的过程分为两步

  1. 任务按照截止日期进行排序
  2. 考虑当前可行的任务集合中权重最大的

以下是一个任务调度问题的例子:

假设有n个任务需要完成,每个任务结束的期限为 di,完成该任务可以得到的价值为 vi。而且一个任务只能完成一次。为了让收益最高,应该如何安排完成任务的时间呢?

以下是该问题的贪心算法解题步骤:

  1. 按照任务的截止日期从小到大排序, 如果截止期限相等,按完成任务的收益从大到小排序。
  2. 依次考虑每个任务,如果该任务的截止时间在当前时间之前,那么该任务不能执行。否则,该任务可以执行,在可行任务集合中,选择具有最大权重的任务进行执行。

下面是任务调度问题的java代码示例:

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

public class TaskSchedule {

    static class Task {
        int deadline;
        int weight;

        public Task(int deadline, int weight) {
            this.deadline = deadline;
            this.weight = weight;
        }

        @Override
        public String toString() {
            return "Task{" +
                    "deadline=" + deadline +
                    ", weight=" + weight +
                    '}';
        }
    }

    public static List<Task> selectTask(List<Task> tasks) {
        Collections.sort(tasks, new Comparator<Task>() {
            @Override
            public int compare(Task o1, Task o2) {
                return o1.deadline != o2.deadline ?
                        Integer.compare(o1.deadline, o2.deadline) : Integer.compare(o2.weight, o1.weight);
            }
        });

        List<Task> result = new ArrayList<>();
        boolean[] slot = new boolean[tasks.size()];
        for (Task task : tasks) {
            for (int i=Math.min(task.deadline, tasks.size()) - 1; i>=0; i--) {
                if (!slot[i]) {
                    result.add(task);
                    slot[i] = true;
                    break;
                }
            }
        }
        return result;
    }

    public static void main(String[] args) {
        List<Task> list = new ArrayList<>();
        list.add(new Task(5, 10));
        list.add(new Task(4, 20));
        list.add(new Task(2, 15));
        list.add(new Task(3, 30));
        list.add(new Task(4, 18));
        list.add(new Task(5, 5));
        List<Task> result = selectTask(list);
        for (Task task : result) {
            System.out.println(task);
        }
    }
}

运行上面的代码片段,输出结果如下:

Task{deadline=2, weight=15}
Task{deadline=3, weight=30}
Task{deadline=4, weight=18}
Task{deadline=5, weight=10}

该算法的复杂度为O(n^2),但是由于计算机的速度非常快,因此计算时间非常短。

零钱问题

零钱问题是指如何用最少的数量的硬币来凑出给定的金额。并且硬币的数量无限。

以下是该问题的贪心算法解题步骤:

  1. 建立存储结果的list,同时建立一个coins数组,存储硬币的面值
  2. 建立变量amount,用于存储当前剩余的金额
  3. 对于每一种硬币,显然选择数量最少的硬币是一个贪心的选择
  4. 因此,每次选择当前硬币最大的硬币面值coin,判断coin是否小于amount,如果是,则将coin添加到结果集中,同时amount减去coin
  5. 重复步骤4,在所有的硬币面值中,反复选择当前面值最大的硬币,直到amount为0

以下是零钱问题的java代码示例:

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

public class CoinChange {

    public static List<Integer> coinChange(int[] coins, int amount) {
        List<Integer> result = new ArrayList<>();
        int[] counts = new int[coins.length];
        for (int i=coins.length - 1; i>=0; i--) {
            int count = amount / coins[i];
            counts[i] = count;
            amount -= count * coins[i];
        }
        for (int i = 0; i < counts.length; i++) {
            for (int j = 0; j < counts[i]; j++) {
                result.add(coins[i]);
            }
        }
        return result;
    }

    public static void main(String[] args) {
        int[] coins = {1, 5, 10, 25};
        int amount = 46;
        List<Integer> result = coinChange(coins, amount);
        System.out.println(result);
    }
}

运行上面的代码片段,输出结果如下:

[25, 10, 10, 1]

该算法的复杂度为O(n),非常高效。

结论

贪心算法是一种非常有用的算法,它能够在大多数情况下产生最优解或近似最优解。但是,贪心算法的正确性需要通过证明来实现,同时贪心算法往往需要先建立数学模型然后再建立算法。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java 数据结构与算法系列精讲之贪心算法 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • 在matlab中创建类似字典的数据结构方式

    当需要使用类似字典的数据结构时,Matlab中可以使用结构体来实现。结构体是一种有序的数据集合,每个元素都可以包含不同类型的数据(如字符串、数值等),并通过指定一个名称来唯一地标识该元素。 创建一个空结构体 使用struct函数可以创建一个空的结构体,可以使用下面的代码: st = struct; 添加键值对 可以将键值对添加到结构体中,可以使用下面的代码向…

    数据结构 2023年5月17日
    00
  • C++ 数据结构超详细讲解单链表

    C++ 数据结构超详细讲解单链表 什么是单链表 单链表是一种常见的线性数据结构,它由若干个节点组成,每个节点包含两部分内容:数据域和指针域。其中数据域存储节点所携带的数据,而指针域存储下一个节点的地址。 单链表的性质在于每个节点只有一个指针域,而第一个节点叫做头节点,通常不存放数据,只用来标注链表的起始位置。最后一个节点的指针域指向 NULL,即表示链表的结…

    数据结构 2023年5月17日
    00
  • java数据结构基础:算法

    Java数据结构基础:算法攻略 概述 在程序员的日常开发中,算法是一项重要的技能,而数据结构则是算法不可缺少的基础。本文将讲解Java数据结构中的基本算法,包括常见算法的实现,算法的分析及算法的运用。经过本文的学习,读者可以掌握Java中基础的算法实现及应用。 常见算法实现 排序算法 排序算法是算法中最基础的一类,常用的算法有冒泡排序、插入排序、选择排序、快…

    数据结构 2023年5月17日
    00
  • C语言全面梳理结构体知识点

    C语言全面梳理结构体知识点 什么是结构体? 结构体是一种自定义的数据类型,它可以包含多个不同类型的成员变量,并且这些成员变量可以通过一个变量名来访问。结构体的定义需要使用关键字struct,并且需要指定结构体的类型名和成员变量。例如: struct Person { char name[20]; int age; float height; }; 以上代码就…

    数据结构 2023年5月17日
    00
  • C++高级数据结构之并查集

    C++高级数据结构之并查集 什么是并查集 并查集(Union Find Set)是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。 并查集定义了如下的三种操作: 1、makeSet(s):建立一个新的并查集,其中包含s个单元素集合。 2、unionSet(x, y):把元素x和元素y所在的集…

    数据结构 2023年5月17日
    00
  • Java数据结构之链表的概念及结构

    Java数据结构之链表的概念及结构 链表的概念 链表是一种非顺序存储的容器,它由一个个结点组成,每个结点包含两部分,数据域和指针域。数据域是存储数据的部分,指针域是指向下一个结点的位置。 相比于数组,链表插入和删除操作的时间复杂度更低,但是访问元素时需要遍历整个链表,时间复杂度相对较高。 链表的结构 链表结构包含两个重要的部分:结点和链表。 结点(Node)…

    数据结构 2023年5月16日
    00
  • mysql的Buffer Pool存储及原理解析

    下面我就来详细讲解一下“mysql的Buffer Pool存储及原理解析”的攻略。 Buffer Pool简介 在MySQL中,Buffer Pool是一个重要的概念,也可以说是MySQL最重要的性能优化建议之一。Buffer Pool是MySQL内存中缓存数据页的数据结构,用于加速数据的读写。 数据页 在MySQL中,数据是以数据页(page)为单位进行读…

    数据结构 2023年5月17日
    00
  • 【牛客小白月赛70】A-F题解【小d和超级泡泡堂】【小d和孤独的区间】【小d的博弈】【小d和送外卖】

    比赛传送门:https://ac.nowcoder.com/acm/contest/53366 难度适中。 ? 作者:Eriktse? 简介:19岁,211计算机在读,现役ACM银牌选手?力争以通俗易懂的方式讲解算法!❤️欢迎关注我,一起交流C++/Python算法。(优质好文持续更新中……)?? 阅读原文获得更好阅读体验:https://www.erikt…

    算法与数据结构 2023年4月17日
    00
合作推广
合作推广
分享本页
返回顶部