我正在面试并一直在练习一些练习题。问题是:
查找数组中重复次数最多的整数。
这是我创建的功能和他们创建的功能。它们被恰当地命名。
var arr = [3, 6, 6, 1, 5, 8, 9, 6, 6]
function mine(arr) {
arr.sort()
var count = 0;
var integer = 0;
var tempCount = 1;
var tempInteger = 0;
var prevInt = null
for (var i = 0; i < arr.length; i++) {
tempInteger = arr[i]
if (i > 0) {
prevInt = arr[i - 1]
}
if (prevInt == arr[i]) {
tempCount += 1
if (tempCount > count) {
count = tempCount
integer = tempInteger
}
} else {
tempCount = 1
}
}
console.log("most repeated is: " + integer)
}
function theirs(a) {
var count = 1,
tempCount;
var popular = a[0];
var temp = 0;
for (var i = 0; i < (a.length - 1); i++) {
temp = a[i];
tempCount = 0;
for (var j = 1; j < a.length; j++) {
if (temp == a[j])
tempCount++;
}
if (tempCount > count) {
popular = temp;
count = tempCount;
}
}
console.log("most repeated is: " + popular)
}
console.time("mine")
mine(arr)
console.timeEnd("mine")
console.time("theirs")
theirs(arr)
console.timeEnd("theirs")
结果如下:
most repeated is: 6
mine: 16.929ms
most repeated is: 6
theirs: 0.760ms
是什么让我的功能比他们慢?
答案 0 :(得分:2)
看起来这不是一个公平的考验。首先运行函数时,它会对数组进行排序。这意味着它们的功能最终使用已经排序的数据,但不会花费执行排序的时间成本。我尝试交换测试运行的顺序,并得到几乎相同的时间:
console.time("theirs")
theirs(arr)
console.timeEnd("theirs")
console.time("mine")
mine(arr)
console.timeEnd("mine")
most repeated is: 6
theirs: 0.307ms
most repeated is: 6
mine: 0.366ms
此外,如果您使用两个单独的阵列,您将看到您的功能和他们的功能大约在相同的时间内运行。
最后,请参阅Anders的回答 - 它表明较大的数据集显示您的函数的O(n * log(n))+ O(n)性能与其函数的O(n ^ 2)性能相比。
答案 1 :(得分:2)
当我测试(JSFiddle)一个包含50 000个元素的随机数组时,我得到以下结果:
mine: 28.18 ms
theirs: 5374.69 ms
换句话说,您的算法似乎要快得多。这是预期的。
首先对数组进行排序,然后循环一次。 Firefox使用merge sort,Chrome使用quick sort的变体(根据此question)。两者平均花费O(n*log(n))
时间。然后循环遍历数组,花费O(n)
时间。总共得到O(n*log(n)) + O(n)
,可以简化为O(n*log(n))
。
O(n^2)
。换句话说,它更慢。
那么为什么你的测试结果与我的不同?我看到了很多可能性:
console.log
运行)占主导地位。这可以使结果看起来完全随机。进一步说明:您不应使用.sort()
对数字进行排序,因为它会按字母顺序排序。相反,请使用.sort(function(a, b){return a-b})
。阅读更多here。
关于进一步说明的进一步说明:在这种特殊情况下,仅使用.sort()
可能实际上更聪明。由于您不关心排序,只关心分组,因此将数字排序错误无关紧要。它仍然会将具有相同值的元素组合在一起。如果没有比较功能(我怀疑它是)更快,那么没有一个排序是有意义的。
您已在O(n*log(n))
中解决了问题,但您可以在O(n)
中完成此操作。这样做的算法非常直观。循环遍历数组,并跟踪每个数字出现的次数。然后选择出现次数最多的数字。
让我们说数组中有m
个不同的数字。循环遍历数组需要O(n)
并找到最大值O(m)
。从O(n) + O(m)
开始,这会将O(n)
简化为m < n
。
这是代码:
function anders(arr) {
//Instead of an array we use an object and properties.
//It works like a dictionary in other languages.
var counts = new Object();
//Count how many of each number there is.
for(var i=0; i<arr.length; i++) {
//Make sure the property is defined.
if(typeof counts[arr[i]] === 'undefined')
counts[arr[i]] = 0;
//Increase the counter.
counts[arr[i]]++;
}
var max; //The number with the largest count.
var max_count = -1; //The largest count.
//Iterate through all of the properties of the counts object
//to find the number with the largerst count.
for (var num in counts) {
if (counts.hasOwnProperty(num)) {
if(counts[num] > max_count) {
max_count = counts[num];
max = num;
}
}
}
//Return the result.
return max;
}
在0到49之间的50 000个元素的随机数组上运行它在我的计算机上只需3.99毫秒。换句话说,它是最快的。背面是你需要O(m)
个记忆来存储每个号码出现的时间。
答案 2 :(得分:0)
这里的其他答案已经很好地解释了为什么theirs
更快 - 以及如何优化你的。使用大型数据集(@Anders)实际上更好。我设法优化theirs
解决方案;也许这里有用的东西。
通过采用一些基本的JS微优化,我可以获得始终如一的更快结果。这些优化也可以应用于您的原始函数,但我将它们应用于theirs
。
>= 0
is very fast.对于这个测试,我的计算机得分为514,271,438
ops / sec,而下一个最快的得分198,959,074
。length
的结果 - 对于较大的数组,这会使better
明显快于theirs
代码:
function better(a) {
var top = a[0],
count = 0,
i = len = a.length - 1;
while (i--) {
var j = len,
temp = 0;
while (j--) {
if (a[j] == a[i]) ++temp;
}
if (temp > count) {
count = temp;
top = a[i];
}
}
console.log("most repeated is " + top);
}
[fiddle]
与theirs
非常相似(如果不相同),但使用上述微优化。
以下是运行每个功能500次的结果。在运行任何函数之前对数组进行预排序,并从sort
中删除mine()
。
mine: 44.076ms
theirs: 35.473ms
better: 32.016ms