前言:
大家好,我是Felix。今天我们通过田忌赛马的这样一个生动的例子来了解贪心算法。每当想起这个生动的例子,理解贪心算法就会相对形象一些了。
题意
已知有两个整数数组A,B, 如果A[i] > B[i], 则称A优势于B。请你返回一个A数组的排列,对于B数组的优势最大。
这是原本的题意,但是本质上就是田忌赛马的例子。比如说你和对方都有3匹马,每匹马都有自己的战力,如何对面的出场顺序确定,你如何能够保证自己获得最大的场次呢?
那么关键点是什么呢?
------你要让自己战力最小的马尽可能的赢得比赛,如果无法赢得则直接匹配对方最强的马。
如上图所示,右边是你拥有的马匹,左图是对方的。经过排序之后,你应该要让自己最小的匹配对方最小的,尽量让战力最低的马匹赢得比赛。在这个例子中,6的这匹马,我们应该就要舍弃,去匹配对方最强的马匹35,因为6无论如何它都赢不了的。所以,这个贪心算法就贪在尽量让自己战力低的马匹也能赢。
如何解题呢
思路:
* 刚才我们已经分析过了,首先这两个数组要进行排序,这样我才容易比较。注意:要保留B数组原本的顺序,因为我们后面获取结果还需要依赖它
* 我们来遍历排序好的A数组,去匹配排序好的B数组,如果A>B, 这时候,我们应该记录这个A的值,如何记录呢
* 应该创建一个链表,挂在这个B的值后面,组成一个MAP
* 如果这个A<B,则我们应该舍弃这个A,放入一个队列,作为被舍弃的马
可能,听起来比较抽象。我们可以看下具体代码。
public class RaceHorses {
public static void main(String[] args) {
int [] A = {6, 14, 27, 19, 10};
int [] B = {9, 35, 17, 13, 7};
int[] ans = raceHorses(A, B);
for (int n : ans)
System.out.print(n + " ");
}
public static int[] raceHorses(int [] numsA, int[] numsB) {
// 对于AB进行排序,但是B要保留原本的顺寻
int[] cloneB = numsB.clone();
Arrays.sort(numsA);
Arrays.sort(numsB);
// 创建一个保存结果的数组
int[] ans = new int[numsA.length];
// 创建一个MAP, key是B的值,value是这个B 可以匹配的值
// 为啥是个数组呢?
// 因为B中不能排除有一样的值,这样,维护起来,链表会更加方便
// 好的,先来初始化这个map
Map<Integer, LinkedList<Integer>> map = new HashMap<>();
for (int b: numsB)
map.put(b, new LinkedList<>());
// 再创建一个垃圾桶,用来存放无法赢得比赛的
LinkedList<Integer> bin = new LinkedList<>();
// 现在来遍历A数组,逐个匹配
int j = 0;//j 指的是B数组的索引,匹配了就往后走
for (int i = 0; i < numsA.length; i++) {
// A匹配上了,放到B对应的结果链表里面
if (numsA[i] > numsB[j]) {
map.get(numsB[j]).add(numsA[i]);
j ++;
} else {
//没有匹配上,放入垃圾箱
bin.add(numsA[i]);
}
}
// 这时候我们维护的map已经出结果了,根据原本B的顺序去生成结果
for (int i = 0; i < cloneB.length; i++) {
LinkedList<Integer> queue = map.get(cloneB[i]);
// 如果QUEUE有元素,说明匹配了,弹出来一个就行
if (!queue.isEmpty())
ans[i] = queue.poll();
// 如果QUEUE没有元素,说明匹配不上,从垃圾桶弹出一个即可
else
ans[i] = bin.poll();
}
return ans;
}
}
最后的输出结果:
好啦,今天的算法分享到这里啦!