Java 数据结构与算法系列精讲之贪心算法
什么是贪心算法?
在计算机科学中,贪心算法是一种通过选择局部最优解来实现全局最优解的优化算法。贪心算法在解决某些最优化问题时非常有效,贪心算法能够达到接近最优解,有时甚至能够达到最优解。
贪心算法解题步骤:
- 建立算法模型
- 找出最优解的子结构
- 设计贪心选择策略
- 实现贪心选择策略为一个最优解
- 证明贪心算法的正确性
贪心算法应用场景
贪心算法应用场景非常广泛,常见的有:
- 任务调度问题
- 找零钱问题
- 最短路径问题
- Huffman编码问题
任务调度问题
一般而言,在任务调度问题中,我们将调度任务的过程分为两步
- 任务按照截止日期进行排序
- 考虑当前可行的任务集合中权重最大的
以下是一个任务调度问题的例子:
假设有n个任务需要完成,每个任务结束的期限为 di,完成该任务可以得到的价值为 vi。而且一个任务只能完成一次。为了让收益最高,应该如何安排完成任务的时间呢?
以下是该问题的贪心算法解题步骤:
- 按照任务的截止日期从小到大排序, 如果截止期限相等,按完成任务的收益从大到小排序。
- 依次考虑每个任务,如果该任务的截止时间在当前时间之前,那么该任务不能执行。否则,该任务可以执行,在可行任务集合中,选择具有最大权重的任务进行执行。
下面是任务调度问题的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),但是由于计算机的速度非常快,因此计算时间非常短。
零钱问题
零钱问题是指如何用最少的数量的硬币来凑出给定的金额。并且硬币的数量无限。
以下是该问题的贪心算法解题步骤:
- 建立存储结果的list,同时建立一个coins数组,存储硬币的面值
- 建立变量amount,用于存储当前剩余的金额
- 对于每一种硬币,显然选择数量最少的硬币是一个贪心的选择
- 因此,每次选择当前硬币最大的硬币面值coin,判断coin是否小于amount,如果是,则将coin添加到结果集中,同时amount减去coin
- 重复步骤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技术站