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日

相关文章

  • C语言数据结构系列之树的概念结构和常见表示方法

    C语言数据结构系列之树的概念结构和常见表示方法 树是一种非线性数据结构,它由若干个节点构成,节点之间通过边来连接,具有层次关系。 树的基本概念和术语 节点:树中的元素,它可以包含一个数据元素或多个数据元素,一个节点也可以称为一个分支节点。 根节点:树的最上层节点,它没有父节点。 叶子节点:没有子节点的节点称为叶子节点。 父节点和子节点:父节点是某个节点的上一…

    数据结构 2023年5月17日
    00
  • C语言数据结构之队列的定义与实现

    C语言数据结构之队列的定义与实现 什么是队列 队列是一种特殊的线性数据结构,它只允许在队列的头部进行删除操作,在队列的尾部进行插入操作,这种操作方式被成为“先进先出”或“FIFO(first in first out)”。 队列的实现方式 队列可以通过数组和链表两种方式进行实现。 1. 数组实现 数组实现队列时,可以定义一个存放元素的数组,同时需要记录队列的…

    数据结构 2023年5月17日
    00
  • TypeScript数据结构栈结构Stack教程示例

    下面就给您详细讲解一下“TypeScript数据结构栈结构Stack教程示例”的完整攻略。 1. 栈结构(Stack)概述 栈是一种特殊的数据结构,它的特点是后进先出(Last In First Out,LIFO)。和数组不同的是,栈只能在栈顶插入和删除元素。栈的常见操作有“- push() 元素入栈,将元素放到栈顶- pop() 元素出栈,从栈顶取出元素…

    数据结构 2023年5月17日
    00
  • C++如何实现BitMap数据结构

    下面我将详细讲解C++如何实现BitMap数据结构的完整攻略,包含以下几个方面: 什么是BitMap数据结构 如何使用C++实现BitMap数据结构 BitMap数据结构的应用示例说明 1. 什么是BitMap数据结构 BitMap数据结构也叫位图,是一种非常简单而高效的数据结构,它主要是用来对大量数字进行存储和操作的。所谓BitMap,就是将一个数字序列通…

    数据结构 2023年5月17日
    00
  • 数据结构之哈夫曼树与哈夫曼编码

    一、背景 编码是信息处理的基础(重新表示信息)。 普通的编码是等长编码,例如7位的ASCIL编码,对出现频率不同的字符都使用相同的编码长度。但其在传输和存储等情况下编码效率不高。 可使用不等长编码,来压缩编码:高频字符编码长度更短,低频字符编码长度更长。   [例] 将百分制的考试成绩转换成五分制的成绩 按顺序分别编码。 按频率分别编码(高频短编码,类似于香…

    算法与数据结构 2023年4月17日
    00
  • 带你了解Java数据结构和算法之数组

    带你了解Java数据结构和算法之数组 在本教程中,我们将学习Java中的数组数据结构和对应的算法。让我们先来了解什么是数组。 什么是数组? 数组是一个同类型数据元素的集合,在内存中连续存储。数组具有索引性,我们可以使用索引值来访问数组中的元素。 声明和初始化数组 在Java中,声明一个数组需要指定以下三个参数: 数组的类型 数组的名称 数组的大小 以下是一个…

    数据结构 2023年5月17日
    00
  • JavaScript数据结构之链表的实现

    JavaScript数据结构之链表的实现 什么是链表 链表是一种线性数据结构,其中的元素在内存中不连续地存储,每个元素通常由一个存储元素本身的节点和一个指向下一个元素的引用组成。相比较于数组,链表具有如下优缺点: 优点:动态地添加或删除元素时,无需移动其它元素。(数组则需要移动其它元素) 缺点:不能随机地访问某个元素,必须从头开始顺序遍历。(而数组可以通过索…

    数据结构 2023年5月17日
    00
  • 设要采用CRC编码传送的数据信息x=1001,当生成多项式为G(x)=1101时,请写出它的循环校验码。若接收方收到的数据信息x’ =1101,说明如何定位错误并纠正错误

    题目:设要采用CRC编码传送的数据信息x=1001,当生成多项式为G(x)=1101时,请写出它的循环校验码。若接收方收到的数据信息x’ =1101,说明如何定位错误并纠正错误 根据题目描述,需要采用CRC编码对数据信息x=1001进行编码,生成多项式为G(x)=1101。下面是计算循环冗余校验码的步骤:1.首先将数据信息x乘以x的次数,使得它的位数与G(x…

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