【问题标题】:Optimize- get third largest num in array优化 - 获得数组中第三大的数字
【发布时间】:2016-09-14 06:49:03
【问题描述】:

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

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

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

【问题讨论】:

  • @jtbandes:看看 OP 的代码。
  • 这很尴尬。
  • 您可以维护一个由 3 个元素组成的小数组:向数组中添加另一个元素(如果不同)、排序、切分第 4 个元素。这样,解决方案的复杂性将与原始数组中的元素数量成线性关系。
  • 你可能想看看实现Introselect

标签: javascript arrays optimization


【解决方案1】:

由于您已经对数组进行了排序,看来您应该可以很好地遍历列表并跟踪您已经看到的数字。当您看到三个不同的数字时,返回当前的一个:

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

【讨论】:

    【解决方案2】:

    使用Set 删除重复项的单行“r”

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

    Set 现在有reasonable browser support

    【讨论】:

    • 不错,这很漂亮。
    • 是的。更紧[...new Set(arr)].sort((a,b) =&gt; a-b)[2]
    • 这是一个潜在的越界访问,而不是“漂亮”。
    • @zerkms 越界?我错过了什么?哦...[1,1,1,1] 会呕吐。很棒的收获!
    • @zerkms 指的是如果arr = [1, 1, 1, 1] 会发生什么。但无论如何,这个问题在这种情况下是不合适的,所以抛出错误是正确的做法。
    【解决方案3】:

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

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

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

    【讨论】:

    • 你的回答值得被接受,你是唯一一个解释排序是矫枉过正的人。
    【解决方案4】:

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

    已编辑:

    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

    【讨论】:

    • a + b 应该是b - a
    • 或者更清楚,使用Map
    • 你能解释一下这是做什么的吗,我不太明白arr.forEach(function(num) { map[num] = ++map[num] || 1; });的语法,键的值是1还是+1?
    • 编辑了我的答案,它是一种创建对象的方法,其中数组中的数字是键,并且每个数字只能有一个键,这只是摆脱重复项的一种方法。
    • 为什么不只是map[num] = true
    【解决方案5】:

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

    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
    

    【讨论】:

      【解决方案6】:

      一个单次迭代 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 项数组,并始终在大约 45 毫秒内返回正确的结果。一个 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);

      【讨论】:

        猜你喜欢
        • 2020-11-21
        • 1970-01-01
        • 2013-05-28
        • 2014-11-17
        • 1970-01-01
        • 2017-06-12
        • 2021-12-04
        • 1970-01-01
        • 2021-10-11
        相关资源
        最近更新 更多