我需要更优化的javascript代码才能找到数组中最大的3个元素。 我试过这段代码。
var maxIndex = new Array();
var maxPoints = new Array();
var scoreByPattern = new Array(93,17,56,91,98,33,9,38,55,78,29,81,60);
function findLargest3(){
maxPoints[0]=0;
maxPoints[1]=0;
maxPoints[2]=0;
for(i=0;i < scoreByPattern.length; i++){
if( scoreByPattern[i] > maxPoints[0]){
maxPoints[0] = scoreByPattern[i];
maxIndex[0]=i;
}
}
for(i=0;i < scoreByPattern.length; i++){
if( scoreByPattern[i] > maxPoints[1] && scoreByPattern[i]< maxPoints[0] ){
maxPoints[1] = scoreByPattern[i];
maxIndex[1]=i;
}
}
for(i=0;i < scoreByPattern.length; i++){
if( scoreByPattern[i] > maxPoints[2] && scoreByPattern[i]< maxPoints[1] ){
maxPoints[2] = scoreByPattern[i];
maxIndex[2]=i;
}
}
alert(scoreByPattern+"/******/"+maxPoints[0]+"/"+maxPoints[1]+"/"+maxPoints[2]);
//alert(maxIndex);
}
如何优化以上? (我需要最大数字的索引)是否有其他简单的方法来解决这个问题?
答案 0 :(得分:9)
修改后的版本
我修改了我的答案,使其更通用。它搜索数组中n个最大元素数的索引:
var scoreByPattern = [93,255,17,56,91,98,33,9,38,55,78,29,81,60];
function findIndicesOfMax(inp, count) {
var outp = [];
for (var i = 0; i < inp.length; i++) {
outp.push(i); // add index to output array
if (outp.length > count) {
outp.sort(function(a, b) { return inp[b] - inp[a]; }); // descending sort the output array
outp.pop(); // remove the last index (index of smallest element in output array)
}
}
return outp;
}
// show original array
console.log(scoreByPattern);
// get indices of 3 greatest elements
var indices = findIndicesOfMax(scoreByPattern, 3);
console.log(indices);
// show 3 greatest scores
for (var i = 0; i < indices.length; i++)
console.log(scoreByPattern[indices[i]]);
这是jsFiddle
答案 1 :(得分:6)
不对大数组进行排序:考虑到大数组,在O(n)
中运行。返回带有[x,y]
的数组数组,其中x
是大数组中的索引值y
。
var ar = [3,172,56,91,98,33,9,38,55,78,291,81,60];
console.log(`input is: ${ar}`);
function getMax(ar){
if (ar.length < 3) return ar;
var max = [[ar[0],0],[ar[1],1],[ar[2],2]],
i,j;
for (i = 3;i<ar.length;i++){
for (j = 0;j<max.length;j++){
if (ar[i] > max[j][0]){
max[j] = [ar[i],i];
if (j<2){
max.sort(function(a,b) { return a[0]-b[0]; });
}
break;
}
}
}
return max;
}
result = getMax(ar);
console.log('output [number,index] is:');
console.log(result);
答案 2 :(得分:5)
对数组进行降序排序,然后获取它的前三个元素:
var maxPoints = new Array();
var scoreByPattern = new Array(93,17,56,91,98,33,9,38,55,78,29,81,60);
findLargest3();
function findLargest3(){
// sort descending
scoreByPattern.sort(function(a,b) {
if (a < b) { return 1; }
else if (a == b) { return 0; }
else { return -1; }
});
alert(scoreByPattern+"/******/"+scoreByPattern[0]+"/"+scoreByPattern[1]+"/"+scoreByPattern[2]);
}
答案 3 :(得分:3)
默认的javascript排序回调效果不佳,因为它在lexicographical order中排序。 10将在5之前(因为1)
我不相信,但是:
my_array.sort(function(a,b) {
return a-b;
});
答案 4 :(得分:2)
假设分布相当正常,这应该是相当优化的:
var max_three, numbers = new Array(93,17,56,91,98,33,9,38,55,78,29,81,60);
max_three = (function (numbers) {
var i, one, two, three;
one = -9999;
two = -9999;
three = -9999;
for (i = 0; i < numbers.length; i += 1) {
num = numbers[i];
if (num > three) {
if (num >= two) {
three = two;
if (num >= one) {
two = one;
one = num;
}
else {
two = num;
}
}
else {
three = num;
}
}
}
return [one, two, three]
}(numbers))
document.write(max_three)
98,93,91
答案 5 :(得分:1)
为什么不对它进行排序并采用第一个(或最后一个按升序排序)三个元素。
var maxPoints = new Array();
var scoreByPattern = new Array(93,17,56,91,98,33,9,38,55,78,29,81,60);
scoreByPattern.sort();
maxPoints[0] = scoreByPattern[scoreByPattern.length - 1];
maxPoints[1] = scoreByPattern[scoreByPattern.length - 2];
maxPoints[2] = scoreByPattern[scoreByPattern.length - 3];
修改强>
如果你需要最大数组的indeces,你可以制作一个你排序的副本,然后找到原始数组中的索引:
var scoreByPattern = new Array(93,17,56,91,98,33,9,38,55,78,29,81,60);
// Make a copy of the original array.
var maxPoints = scoreByPattern.slice();
// Sort in descending order.
maxPoints.sort(function(a, b) {
if (a < b) { return 1; }
else if (a == b) { return 0; }
else { return -1; }
});
// Find the indices of the three largest elements in the original array.
var maxPointsIndices = new Array();
maxPointsIndices[0] = scoreByPattern.indexOf(maxPoints[0]);
maxPointsIndices[1] = scoreByPattern.indexOf(maxPoints[1]);
maxPointsIndices[2] = scoreByPattern.indexOf(maxPoints[2]);
找到没有排序的索引的另一种方法是:
var scoreByPattern = new Array(93,17,56,91,98,33,9,38,55,78,29,81,60);
var maxIndices = new Array(Number.MIN_VALUE, Number.MIN_VALUE, Number.MIN_VALUE);
for (var i = 0; i < scoreByPattern.length; i++) {
if (maxIndices[0] < scoreByPattern[i]) {
maxIndices[2] = maxIndices[1];
maxIndices[1] = maxIndices[0];
maxIndices[0] = scoreByPattern[i];
}
else if (maxIndices[1] < scoreByPattern[i]) {
maxIndices[2] = maxIndices[1];
maxIndices[1] = scoreByPattern[i];
}
else if (maxIndices[2] < scoreByPattern[i]) maxIndices[2] = scoreByPattern[i];
}
答案 6 :(得分:1)
这个简单的衬垫将解决您的问题。
[1, -5, 2, 8, 17, 0, -2].sort(function(a, b){return b - a}).slice(0, 3)
所以,如果你有一个数组,并希望找到N个最大值:
arr.sort(function(a, b){return b - a}).slice(0, n)
对于N个最小值:
arr.sort(function(a, b){return a - b}).slice(0, n)
答案 7 :(得分:1)
这是针对您的问题的优化解决方案,无需使用排序或其他复杂的数组方法:
var maxIndex = new Array();
var maxPoints = new Array();
var scoreByPattern = new Array(93,17,56,91,98,33,9,38,55,78,29,81,60);
function findTop3(n) {
for (var i = 0; i < n.length; i ++) {
if (i === 0) {
maxPoints.push(n[i]);
maxIndex.push(i);
} else if (i === 1) {
if (n[i] > maxPoints[0]) {
maxPoints.push(maxPoints[0]);
maxPoints[0] = n[i];
maxIndex.push(maxIndex[0]);
maxIndex[0] = i;
} else {
maxPoints.push(n[i]);
maxIndex.push(i);
}
} else if (i === 2) {
if (n[i] > maxPoints[0]) {
maxPoints.push(maxPoints[0]);
maxPoints[1] = maxPoints[0];
maxPoints[0] = n[i];
maxIndex.push(maxIndex[0]);
maxIndex[1] = maxIndex[0];
maxIndex[0] = i;
} else {
if (n[i] > maxPoints[1]) {
maxPoints.push(maxPoints[1]);
maxPoints[1] = n[i];
maxIndex.push(maxIndex[1]);
maxIndex[1] = i;
} else {
maxPoints.push(n[i]);
maxIndex.push(i);
}
}
} else {
if (n[i] > maxPoints[0]) {
maxPoints[2] = maxPoints[1];
maxPoints[1] = maxPoints[0];
maxPoints[0] = n[i];
maxIndex[2] = maxIndex[1];
maxIndex[1] = maxIndex[0];
maxIndex[0] = i;
} else {
if (n[i] > maxPoints[1]) {
maxPoints[2] = maxPoints[1];
maxPoints[1] = n[i];
maxIndex[2] = maxIndex[1];
maxIndex[1] = i;
} else if(n[i] > maxPoints[2]) {
maxPoints[2] = n[i];
maxIndex[2] = i;
}
}
}
}
}
findTop3(scoreByPattern);
console.log('Top Elements: ', maxPoints);
console.log('With Index: ', maxIndex);
答案 8 :(得分:0)
var maxPoints = [];
var scoreByPattern = [93,17,56,91,98,33,9,38,55,78,29,81,60];
function cloneArray(array) {
return array.map(function(i){ return i; });
}
function max3(array) {
return cloneArray(array).sort(function(a,b) { return b-a; }).slice(0,3);
}
function min3(array) {
return cloneArray(array).sort(function(a,b) { return a-b; }).slice(0,3);
}
var array=scoreByPattern;
alert("Max:"+ max3(array)[0] +' '+max3(array)[1] +' '+max3(array)[2]);
alert("Min:"+ min3(array)[0] +' '+min3(array)[1] +' '+min3(array)[2]);
答案 9 :(得分:0)
function get3TopItems(arr) {
return arr.sort((a, b) => b - a).slice(0, 3);
}
答案 10 :(得分:0)
以下函数返回一个数组,其元素是一个固定的双元素数组,第一个元素作为索引,第二个元素作为其值。
const searchCriteria = {
startTime: new Date("08/12/2020"),
endTime: new Date("09/12/2020"),
LiveTime: new Date("10/12/2020"),
schedAction_Active: "Active",
siteId: 1825,
scheduleId: 3792
};
const res = new URLSearchParams(searchCriteria).toString();
console.log(res);
答案 11 :(得分:0)
我不敢相信这些答案中有多少比 OP 提供的解决方案更糟糕。大多数都更简洁,但要么:
这对 SO 来说可能有点太长了,但我觉得这是相关信息。
我认为 (3) 表示“我希望方法更简洁/可读”,(2) 表示“索引可直接在输出中使用”,以及 (1) 表示“我希望代码能够尽可能高效”。
从 (3) 开始,因为它对人们最有用:您的原始算法非常有效,但您基本上是在 3 个循环中的每个循环中复制代码;每个循环中唯一的区别实际上是索引发生了变化。如果您只是想减少字符数,您可以将您的算法(有几个小警告1)重写为:
function findLargest3(arry) {
const max = {
indices: new Array(0, 0, 0),
points : new Array(0, 0, 0)
}
// Inner private function to remove duplicate code
// Modifies "max" as a side-effect
function setLargest (idx) {
const lastMax = max.points[idx - 1] || Number.MAX_VALUE;
for (let i = 0; i < arry.length; i++) {
if (arry[i] > max.points[idx] && arry[i] < lastMax) {
max.points[idx] = arry[i];
max.indices[idx] = i;
}
}
}
setLargest(0); // Get's the 0'th largest item
setLargest(1); // etc.
setLargest(2);
return max;
}
let scoreByPattern = new Array(93, 17, 56, 91, 98, 33, 9, 38, 55, 78, 29, 81, 60);
let max = findLargest3(scoreByPattern);
console.log(scoreByPattern + "/******/" + max.points.join("/"));
除了添加函数之外,我唯一真正需要添加的是:const lastMax = max.points[idx - 1] || Number.MAX_VALUE;
。这仅适用于 idx == 0
的情况。由于没有 max.points[-1]
,我们希望第二次检查始终返回 true,因此如果 max.points[idx - 1]
不存在,请通过 ||
将其设置为可能的最高值。
这实际上比您的实现慢了大约 4 倍。这种缓慢是没有直接修改全局状态(更快但更难维护)和包含内部函数而不是代码重复的结果。用原始的重复代码替换内部函数并保持其他所有更改相同,最终执行速度提高了 40%,因为它完全避免了函数的创建和调用;但再次更容易阅读。它还使得将 findLargest3
扩展为 findLargest5
或 findLargestN
变得更加简单。
此外,它应该与您的原始解决方案相同(请参阅我对 1 的回答),这与涉及 sort
的解决方案不同;这意味着它将与您的原始解决方案一样扩展,同时更易于阅读和维护。如果您真的需要性能,那么您的原始解决方案几乎没有问题,我什至认为您的原始解决方案比某些已发布的解决方案更好。 (例如,从粗略测试来看,将数组的大小增加到约 100 个元素会使我的解决方案运行速度降低约 3 倍,但使使用 sort
的解决方案运行速度降低 6 倍以上。
1注意:您的算法有一个主要问题,那就是 i
是“全局范围的”。如果您编写以下内容,它将运行 i==0
,然后永远循环使用 i==scoreByPattern.length
& 永远不会完成:
// Outer loop
for (var i = 0; i <= scoreByPattern.length + 1; i++) {
findLargest3()
}
那是因为您的 for (i = 0 ...
语句没有引用 i
的“本地”版本,并且会回退到先前范围中定义的范围(如果找不到,则返回全局范围) )。您的循环在完成时始终将 i
设置为 scoreByPattern.length
,这会更改此外部循环中的值。如果您尝试在循环期间修改迭代器,某些语言和语言结构(例如 C# 中的 foreach
)实际上不会编译。修复实际上真的很简单,只需在循环声明中说 var i
或 let i
然后 i
将只引用局部于功能。
还有一个小问题,就是你的原始算法忽略了数组中的重复项。如果我将另一个 93
添加到原始数组的末尾,它仍将顶部值返回为 [98, 93, 91]
。我没有在我的解决方案中改变它,因为它可能是故意的(例如,前三个“值”是 98、93 和 91,但 93 恰好出现了两次)。要修复,您只需检查 arry[i] < max.points[...
是否不在 i
中,而不是检查 max.indices
。根据重复的数量,这可能会减慢算法的速度(无需重写),但假设 duplicates << array.length
那么它不会明显变慢。
虽然本身不是“问题”,但拥有“全局状态”(即 scoreByPattern, maxPoints, maxIndex
)然后让它们被函数改变通常并不理想。它可以在计算上更快,但更难维护。这就是为什么我将 maxPoints / maxIndex
的声明移到函数体中并让它请求 scoreByPattern
作为参数的原因。有很多场景都可以(例如对象成员变量和增变器方法)。但在这里,findLargest3
看起来真的像是一个辅助函数/函数,因此理想情况下不应该有副作用(即修改外部状态/传递给它的参数)。
简单提一下 (2):如果索引本身很重要,那么对数组进行排序并没有真正的意义,除非在某些非常特殊的情况下(请参阅我对第 1 点的回答)。排序是直接在数组上使用 Javascript 的 sort
方法的“就地”操作。这意味着它将在大多数这些答案中修改原始数组;所以原始索引实际上丢失了并且对程序的其余部分也有其他后果。为了使用排序来获取前 N 个值,您必须先复制数组,对副本进行排序,取前 3 个值,然后遍历原始数组 ~3 次尝试使用 indexOf
进行匹配或类似(并处理重复项)。
这显然是一种资源浪费,因为其中一些答案比原始答案慢了 30 倍之多。您可以尝试生成“索引”而不是值的列表,根据原始数组对它们进行排序,这样会稍微快一些。但是在这种特定情况下,除非我疯了,否则我们只想要“前 3 名”(再次参见我对第 1 点的回答),排序几乎总是会变慢。
最后,看 (1) 在分析算法时应用称为“Big-O 表示法”的概念很有用。我建议谷歌搜索并阅读,但要广泛:我们想看看算法的效率,随着输入长度的增加。例如。在您的应用程序中,我们有数组 scoreByPattern
,我们会说它的长度为 N
,以及您的问题强加的显式“3 个索引/值”,但它对能够说“返回顶部 M
索引/值”。如果 scoreByPattern
或 top M
非常短,那么算法的复杂度并不重要,但如果它们有成百上千的元素,那么它们的计算复杂度就变得非常重要。
另外,重要的是要说JS中的数组,如果你知道索引,“查找”实际上是一个几乎瞬时的操作~Big O(1)
。此外,在 Big-O 中,我们倾向于只关注分析的“主要”项而忽略任何恒定时间因素。例如。如果解决方案是 10x O(1) + 2x O(N)
(10 个恒定时间操作 + 长度为 N
的列表中的每个项目的 2 个操作),我们说解决方案的 Big-O 是 O(N)
。这是因为,对于相当长的列表,如果我们将 N 的值加倍,所花费的时间将大致加倍。如果它受到 O(N2) 操作的限制并且列表长度加倍,则需要 22 倍(即慢 4 倍)。
所以你原来的解决方案是:
意味着它会像 kO(1) + 3O(cN)
这样的东西,其中 k
和 c
是一些常量,一般将其保留为:O(N)
。如果我们想要“Top M
索引”,您最终会得到:MO(N)
最终是 O(NM)
(即 N * M
的 Big-O)。为什么这很重要?
sort
是 O(N Log2(N))
的 Big-O 这意味着,例如,大约 64 个项目的列表,排序大约需要 64 * Log2(64) == 64 * 6 == ~384ish operations
。因此,将您的解决方案与涉及 sort
的解决方案进行比较,除非 M > Log2(N)
,否则最好不要排序。
从正确的角度来看:如果您的列表有 100 个项目,您需要尝试获得 6 个以上的“最大”项目以提高排序效率。如果是 100,000 个项目,您需要尝试获得超过 16 个“最大”项目。在这个用例中,特别是因为您明确地说了“3”,所以几乎总是不排序更有意义。特别是,因为您正在尝试获取索引;如果您只想要值,那么排序解决方案可能更可取,因为它们很简单。
您可能可以稍微调整一下您的算法以进一步加快速度,但从粗略的测试来看:我认为您的解决方案已经是这里最快的解决方案。如果你想让维护/阅读/扩展更容易,那绝对是可能的;但这真的不应该以实际增加“时间复杂度”为代价(即使解决方案比 O(N)
更糟糕),除非它大大简化了实现。
答案 12 :(得分:0)
我创建了一个函数,该函数返回 N
最大元素的 N
索引(在值相等的情况下,它返回找到的第一个索引)。
在代码中,N
是 qtyMax
。
函数的成本为 O(n)
,在最坏的情况下为 n * qtyMax
。
function maxIdxs(arr, qtyMax) {
var maxIndex = new Array();
for(var i=0; i<arr.length; i++) {
var m=0;
while(m<qtyMax && arr[i]<=arr[maxIndex[m]])
m++;
if(m<qtyMax)
maxIndex.splice(m, 0, i);
}
return maxIndex.slice(0, qtyMax);
}
console.log(maxIdxs( [1,23,46,35,20] , 0 )); // []
console.log(maxIdxs( [] , 3 )); // []
console.log(maxIdxs( [1,23] , 3 )); // [1,0]
console.log(maxIdxs( [1,23,46,35,20] , 3 )); // [2,3,1]
console.log(maxIdxs( [1,40,40,40,40] , 3 )); // [1,2,3]
要获得相应的函数minIdxs
(找到最小的N
索引),您只需要将{{1}的条件中的<=
替换为>=
}.