优化 - 获得数组中的第三大数字

时间:2016-05-18 00:57:24

标签: javascript arrays optimization

所以,我正在努力应对这一挑战,以返回数组中的第三大数字。我已经解决了,直到我意识到我必须考虑到重复的数字。我通过添加3层for变量ijk来处理这个问题。你会在代码中看到我的意思。这不是非常有效或可扩展。

我的问题是,如何优化此代码?我应该使用哪些其他方法?

function thirdGreatest (arr) {
    arr.sort(function(a, b) {
        if (a < b) {
            return 1;
        } else if (a > b) {
            return -1;
        } else {
            return 0;
        }
    });

    for ( var i = 0; i < arr.length; i++) {
        for (var j = 1; j < arr.length; j++) {
            for (var k = 2; k < arr.length; k++) {
                if (arr[i] > arr[j]) {
                    if (arr[j] > arr[k]) {
                        return arr[k];
                    }

                }
            }
        }
    }


}

console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31, 31, 31])); // 23
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31])) // 23
console.log(thirdGreatest([5, 3, 7, 4])); // 4
console.log(thirdGreatest([2, 3, 7, 4])); // 3

6 个答案:

答案 0 :(得分:3)

由于你已经对数组进行了排序,看起来你应该很好地迭代列表并跟踪你已经看过的数字。当您看到三个不同的数字时,请返回当前的数字:

var seen = [arr[0]];

for (var i = 1; i < arr.length; i++) {
  if (arr[i] !== seen[0]) {
    if (seen.length === 2) {
      return arr[i];
    }
    seen.unshift(arr[i]);
  }
}

function thirdGreatest (arr) {
    arr.sort(function(a, b) {
        return b - a;
    });

    var seen = [arr[0]];
    
    for (var i = 1; i < arr.length; i++) {
      if (arr[i] !== seen[0]) {
        if (seen.length === 2) {
          return arr[i];
        }
        seen.unshift(arr[i]);
      }
    }
}

console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31, 31, 31])); // 23
console.log(thirdGreatest([5, 3, 23, 7,3,2,5,10,24,2,31])) // 23
console.log(thirdGreatest([5, 3, 7, 4])); // 4
console.log(thirdGreatest([2, 3, 7, 4])); // 3

注意:您可以将排序回调简化为

arr.sort(function(a, b) {
  return b - a;
});
// With arrow functions:
// arr.sort((a, b) => b - a);

回调必须返回一个更大,更小或等于0的数字,它不必完全是-11

答案 1 :(得分:3)

一个 - &#34;行&#34; r使用Set删除重复项

Array.from(new Set(arr)).sort(function(a, b) {
    return b - a;
})[2];

Set现在有reasonable browser support

答案 2 :(得分:3)

最佳解决方案是在单次通过O(n)时间内完成此操作。您不需要对数组进行排序 - 这样做可以使您的解决方案至少(n log n)。

要在单次传递中执行此操作,您只需要三个临时变量:maximum,secondLargest,thirdLargest。只需遍历数组并根据需要更新这些值(即当你替换最大值时它会变成第二大等等......)。最后,当您看到重复项(即currentValue == secondLargest)时,只需忽略它们。他们不会影响结果。

不要忘记检查边缘情况。您无法为[2,2,2,2,2]或[3,2]提供答案。

答案 3 :(得分:1)

尝试考虑一下您可以在这里使用的数据结构。我建议一套。每次添加嵌套循环时,您的函数都会以指数方式变慢。

<强>编辑:

function thirdGreatest(arr) {
  var s = Array.from(new Set(arr)).sort(function(a, b) {
    return a - b;
  })
  return s[2] || s[1] || s[0] || null;
}

Working Example

我们需要能够处理:

[1,2,1,2] // 2
[1,1,1,1] // 1
[] // null

这假设你传入一个数组。

  • 如果您没有第三大号码,那么您将获得第二个号码。
  • 如果你没有第二大,你就会获得第一大。
  • 如果您没有号码,则会获得null

如果您想要第三大或无,请返回s[2] || null

答案 4 :(得分:0)

许多其他答案需要多次循环初始数组。以下分类和重复删除同时进行。它不那么简洁,但性能更高。

const inputArray = [5,3,23,24,5,7,3,2,5,10,24,2,31,31,31];

const getThirdGreatest = inputArray => {
    const sorted = [inputArray[0]]; // Add the first value from the input to sorted, for our for loop can be normalized.
    let migrated = false;
    let j;
    for(let i = 1; i<inputArray.length; i++) { // Start at 1 to skip the first value in the input array
      for(j=0; j<sorted.length; j++) {
        if(sorted[j] < inputArray[i]) {
            // If the input value is greater than that in the sorted array, add that value to the start of the sorted array
            sorted.splice(j,0,inputArray[i]);
            migrated = true;
            break;
        } else if(sorted[j] === inputArray[i]) {
            // If the input value is a duplicate, ignore it
            migrated = true;
            break;
        }
      }
      if(migrated === false) {
        // If the input value wasn't addressed in the loop, add it to the end of the sorted array.
        sorted[sorted.length] = inputArray[i];
      } else {
        migrated = false;
      }
    }
    // Return the third greatest
    return sorted[2];;
};

const start = performance.now();
getThirdGreatest(inputArray); // 23
const end = performance.now();
console.log('speed: ', end - start); // 0.1 - 0.2ms

答案 5 :(得分:0)

一次迭代O(n)和非常快速的方法就是让你自己的Set像对象一样。有利的一点是,在构建“独特”元素的“排序”列表时,根本不进行比较,从而带来巨大的效率。当处理长度超过1,000,000的巨大列表时,差异非常明显。

var arr = [5, 3, 23, 7,3,2,5,10,24,2,31, 31, 31],
 sorted = Object.keys(arr.reduce((p,c)=> (p[c] = c, p),Object.create(null))),
  third = sorted[sorted.length-3];

document.write(third);

如果您认为Object.keys可能不会返回已排序的数组(我还没有看到它),那么您可以像在Set方法中那样对它进行排序。

在这里,我尝试了1,000,000个项目数组,并在45msecs左右返回正确的结果。 10,000,000个项目阵列需要约450毫秒,比本问题下列出的其他O(n)解决方案少50%。

var arr = [],
 sorted = [],
     t0 = 0,
     t1 = 0,
  third = 0;

for (var i = 0; i<1000000; i++) arr[i] = Math.floor(Math.random()*100);
t0 = performance.now();
sorted = Object.keys(arr.reduce((p,c)=> (p[c] = c, p),Object.create(null)));
third = sorted[sorted.length-3];
t1 = performance.now();
 
document.write(arr.length + " size array done in: " + (t1-t0) + "msecs and the third biggest item is " + third);