【发布时间】:2014-01-02 15:47:04
【问题描述】:
我刚刚完成了以下 Codility Peaks 问题。问题如下:
给出了一个由 N 个整数组成的非空零索引数组 A。 一个峰值是一个比它的邻居大的数组元素。更准确地说,它是一个索引 P,使得 0 A[P + 1]。 比如下面的数组A:
A[0] = 1
A[1] = 2
A[2] = 3
A[3] = 4
A[4] = 3
A[5] = 4
A[6] = 1
A[7] = 2
A[8] = 3
A[9] = 4
A[10] = 6
A[11] = 2
恰好有三个峰值:3、5、10。 我们想把这个数组分成包含相同数量元素的块。更准确地说,我们想选择一个数字 K 来产生以下块: A[0], A[1], ..., A[K - 1], A[K], A[K + 1], ..., A[2K - 1], ... A[N - K], A[N - K + 1], ..., A[N - 1]。 更重要的是,每个区块至少应该包含一个峰。请注意,块的极端元素(例如 A[K - 1] 或 A[K])也可以是峰值,但前提是它们具有两个邻居(包括相邻块中的一个)。 目标是找到可以将数组 A 划分为的最大块数。 数组A可以分成如下块:
一个块(1、2、3、4、3、4、1、2、3、4、6、2)。此块包含三个峰。
两个块 (1, 2, 3, 4, 3, 4) 和 (1, 2, 3, 4, 6, 2)。每个区块都有一个峰值。
三个方块 (1, 2, 3, 4), (3, 4, 1, 2), (3, 4, 6, 2)。每个区块都有一个峰值。
特别注意第一个块 (1, 2, 3, 4) 在 A[3] 处有一个峰值,因为 A[2] A[4],即使 A[4]是在相邻的街区。 但是,数组 A 不能分成四个块,(1, 2, 3), (4, 3, 4), (1, 2, 3) 和 (4, 6, 2),因为 (1, 2, 3) 块不包含峰值。请特别注意 (4, 3, 4) 块包含两个峰:A[3] 和 A[5]。 数组A最多可以分成三个块。
写一个函数: 类解决方案{公共int解决方案(int [] A); } 即,给定一个由 N 个整数组成的非空零索引数组 A,返回 A 可以划分的最大块数。 如果 A 不能被分成一定数量的块,该函数应该返回 0。 例如,给定:
A[0] = 1
A[1] = 2
A[2] = 3
A[3] = 4
A[4] = 3
A[5] = 4
A[6] = 1
A[7] = 2
A[8] = 3
A[9] = 4
A[10] = 6
A[11] = 2
该函数应返回 3,如上所述。 假设:
N 是 [1..100,000] 范围内的整数; 数组 A 的每个元素都是 [0..1,000,000,000] 范围内的整数。
复杂性:
预计最坏情况时间复杂度为 O(N*log(log(N)))
预期的最坏情况空间复杂度为 O(N),超出输入存储(不计算输入参数所需的存储)。
输入数组的元素可以修改。
我的问题
因此,我使用在我看来似乎是蛮力解决方案的方法来解决这个问题 - 从1..N 检查每个组大小,并检查每个组是否至少有一个峰值。前 15 分钟我试图解决这个问题,我试图找出一些更优化的方法,因为所需的复杂度是 O(N*log(log(N)))。
这是我的“蛮力”代码,它通过了所有测试,包括大型测试,得分为 100/100:
public int solution(int[] A) {
int N = A.length;
ArrayList<Integer> peaks = new ArrayList<Integer>();
for(int i = 1; i < N-1; i++){
if(A[i] > A[i-1] && A[i] > A[i+1]) peaks.add(i);
}
for(int size = 1; size <= N; size++){
if(N % size != 0) continue;
int find = 0;
int groups = N/size;
boolean ok = true;
for(int peakIdx : peaks){
if(peakIdx/size > find){
ok = false;
break;
}
if(peakIdx/size == find) find++;
}
if(find != groups) ok = false;
if(ok) return groups;
}
return 0;
}
我的问题是我如何推断这实际上是 O(N*log(log(N))),因为这对我来说一点也不明显,而且我很惊讶我通过了测试用例。我正在寻找即使是最简单的复杂性证明草图,也能让我相信这个运行时。我会假设 log(log(N)) 因子意味着在每次迭代中通过平方根来某种程度地减少问题,但我不知道这如何适用于我的问题。非常感谢您的帮助
【问题讨论】:
-
将 peakIdx 除以大小并不那么聪明,因为二进制数的除法涉及大量的加法 + 移位操作。检查峰值索引是否在当前块的边界索引内要快得多,因为这使用 2 个减法和 1 个逻辑比较操作。
标签: algorithm