给出两个列表A和B,B是A的字谜.B是A的字谜A是通过随机化A中元素的顺序来制作的。我们想找到一个索引映射P,从A到B映射P [i] = j表示A中的第i个元素出现在索引j处的B中。这些列表A和B可能包含重复项。
例如,给定
A = [12,28,46,32,50] B = [50,12,32,46,28] 我们应该回来 [1,4,3,2,0]
我的解决方案是O(n ^ 2)
public int[] anagramMappings(int[] A, int[] B) {
int[] result = new int[100];
int count = 0;
for (int i = 0; i < A.length; i++) {
for (int j = 0; j < B.length; j++) {
if (A[i] == B[j]) {
result[i] = j;
count++;
break;
}
}
}
int[] tempArray = new int[count];
for (int i = 0; i < count; i++) {
tempArray[i] = result[i];
}
return tempArray;
}
这是我认为可能比上述解决方案更有效的另一种解决方案。我这样说是因为我测试了两个不同输出的片段和放大器。第一个片段几乎总是执行得更快。
请说明为什么第一个代码段比第二个代码段更快。我相信第二个在O(n)复杂度方面效率更高
public int[] anagramMappingsx(int[] A, int[] B) {
int[] res = new int[A.length];
int index = 0;
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < B.length; i++) {
if (!map.containsKey(B[i])) {
map.put(B[i], i);
}
}
for (Integer i : A) {
if (map.containsKey(i)) {
res[index++] = map.get(i);
}
}
return res;
}
答案 0 :(得分:7)
Big-O分析假设N非常大。它是关于复杂性随着N变为无穷大而发生的事情。因此,例如,O(n + 100)与O(n)相同。但很明显,对于小N和大常数,这是不正确的。
在您的情况下,您的输入很小,而您的O(n)算法正在使用非常复杂的数据结构,需要散列和表查找(以及处理存储桶未命中以及所有其余的)。你的O(n ^ 2)算法没有做到这一点。从长远来看,地图可以降低成本,但肯定不是短期内的。
通常,对于大多数语言的小数据集,您应该期望数组是可用的最快数据结构,即使它们强制您使用O(n ^ 2)算法。通常需要不止一些元素来弥补更复杂的数据结构的成本。
由于内存局部性和编译器优化,数组也往往比其他数据结构更快(尽管这取决于您的语言)。与大O复杂度分析相比,内存局部性,减少分配/解除分配以及消除动态调度通常会对实际性能产生更大或更大的影响。
令人遗憾的是,CS程序和白板访谈非常关注big-O分析,好像它是性能的开始和结束。性能远远超过算法复杂性
如果你想看到你的O(n)算法烧掉你的O(n ^ 2)算法,试着用10k或10M元素运行它们,而不是5.在那些尺度上,big-O更可能占主导地位情况。