Codility flags solution in Java
本文来源于Codility 的一道题目.原题传送门
Question
在给定一个数组中,存在0 - k个peak节点(值大于左右2个邻居的), 目标是将旗子放到peak的位置,并且两个peak的距离(下标差)必须大于等于总旗子数量。
Brute Force
先从简单的方案入手,要想知道最多能插入旗子的数量,总共需要2个步骤:
- 找到数组所有peak, 放入 peak_list.
- 依次从1到size(peak_list) 尝试是否能够放入指定的旗子.
这个过程可以用以下伪代码来代替,但是注意这只是第一步,还没到正式开始写代码的阶段。
public int solution(int[] A) {
List<Integer> peaks = getAllPeaks(A);
int ans = 0;
for (int i = 1; i < peak.size(); i ++) {
if (canPutFlags(peaks, i)) {
ans = i;
}else{
break;
}
}
return ans;
}
其中canPutFlags
方法可以依次遍历全有的peak, 判断具体是否满足,如果满足则可以放入旗子。最后判断放入的总旗子是否满足条件。
时间复杂度: O(N) + O(N) * O(N) = O(N^2)
Optimize
整个程序瓶颈在于第二阶段,寻找最大旗子的过程。那么我们看下是否能够优化呢?
尝试寻找规律,如果存在满足条件旗子数为K
,那么K - 1
也一定满足。因此可以使用二分法来减少每次搜索的数量。
尝试从中间位置 MID = PEAK_SIZE / 2,分为2种情况:
- 如果MID满足,则0 - MID全部满足条件,无需搜索。
- 如果MID不满足,则MID - PEAK_SIZE全部不满足,无需搜索。
现在我们的代码变成:
public int solution(int[] A) {
List<Integer> peaks = getAllPeaks(A);
return binarySearch(peaks, 1, peaks.size());;
}
时间复杂度: O(N) + O(N) * O(N * log(N)) = O(N * log(N))
总体的复杂度下降了一个量级,下面开始真正写代码.
Implement
一般来说我们最好将代码按照模块化的方式拆分成若干函数,这样做虽然代码量变多,但是好处多多。
- 可读性好
- 扩展性更好
- 易于测试、问题定位
- 实现简单,只需要专注在一个小问题上。
PS:面试时间不够,可以抓大放小,简单的方法和面试官说一下思路。
// you can also use imports, for example:
import java.util.*;
// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");
class Solution {
public int solution(int[] A) {
// write your code in Java SE 8
if (A == null || A.length == 0) {
return 0;
}
// 1. find all peaks.
List<Integer> peaks = getAllPeaks(A);
// find max flags, range from 1 - k
// 2. binary search.
return binarySearch(peaks);
}
private List<Integer> getAllPeaks(int[] A) {
List<Integer> peaks = new ArrayList();
for (int i = 1; i < A.length - 1; i++) {
if (A[i] > A[i + 1] && A[i] > A[i - 1]) {
peaks.add(i);
}
}
return peaks;
}
private Integer binarySearch(List<Integer> peaks) {
int begin = 1, end = peaks.size();
while (begin + 1 < end) {
int mid = begin + (end - begin) / 2;
if (canPutFlags(peaks, mid)) {
begin = mid;
} else {
end = mid;
}
}
if (canPutFlags(peaks, end)) {
return end;
}
if (canPutFlags(peaks, begin)) {
return begin;
}
return 0;
}
public boolean canPutFlags(List<Integer> peaks, int flags) {
if (peaks.size() == 0) {
return false;
}
int lastIndex = peaks.get(0);
int usedFlags = 1;
for (int p = 1; p < peaks.size(); p++) {
int pIndex = peaks.get(p);
if (pIndex - lastIndex >= flags) {
usedFlags++;
lastIndex = pIndex;
}
if (usedFlags == flags) {
return true;
}
}
// ensure only one peek.
return usedFlags == flags;
}
}
Test
设计一些极端的case,避免产生corner case,例如:
- 没有peak
- 只有1个peak
- peak 的距离间隔为1
- …
总结
结果查看: https://app.codility.com/demo/results/trainingKNHF4F-8UV/