【问题标题】:Return the first duplicate number from an array从数组中返回第一个重复的数字
【发布时间】:2018-02-10 22:07:50
【问题描述】:

我正在尝试解决一个简单的挑战,我编写了一个返回数组中第一个重复数字的函数。

这是我尝试过的:

function duplicateNumber(arr) {
    for (var i = 0; i < arr.length; i++) {
        for (var j = arr.length; j >= 0; j--) {
            if (arr[i] === arr[j]) {
                var dup_num = arr[i]
            }
        }
    }
    return dup_num
}

它似乎不起作用。我做错了什么?

刚刚意识到我也在从头到尾循环从头到尾。

在数组中 = [3, 5, 6, 8, 5, 3]

重复号码应该是5,因为它在3之前重复。

【问题讨论】:

  • 发布输入数组
  • 您是否尝试使用debugger 单步执行该代码?
  • 您可以修改或复制数组吗?
  • 道歉,所以我误读了这个问题。在这种情况下:array = [3, 5, 6, 8, 5, 3] 返回值应该是 5,因为它是数组中要复制的第一个值。
  • 这里不需要两个数组。如果您执行arr.sort() 以便对值进行数字排序,则只需将数组中的每个项目与其前一个项目进行比较。如果匹配,则返回。

标签: javascript arrays algorithm array-algorithms


【解决方案1】:

在 ES2015 中,真的很简单

let dupe = arr.find((k,i) => arr.lastIndexOf(k) !== i);

您只需检查索引以查看在此之前是否存在具有相同值的索引,在这种情况下,它将是 第一个 找到 重复

function duplicateNumber(arr) {
	return arr.find((k,i) => arr.indexOf(k) !==i);
}

console.log( duplicateNumber([3, 5, 6, 8, 5, 3]) ) // 5 (not 3)
console.log( duplicateNumber([1, 2, 3, 1, 2, 3]) ) // 1
console.log( duplicateNumber([1, 2, 3, 4, 4, 2]) ) // 4 (not 2)

没有 ES2015

function duplicateNumber(arr) {
  var item = null;

  for (var i = 0; i < arr.length; i++) {
    if (arr.lastIndexOf(arr[i]) !== i) {
      item = arr[i];
      break;
    }
  }

  return item;
}

console.log(duplicateNumber([3, 5, 6, 8, 5, 3])) // 5
console.log(duplicateNumber([1, 2, 3, 1, 2, 3])) // 1
console.log(duplicateNumber([1, 2, 3, 4, 4])) // 4

【讨论】:

  • 在我的测试中有点停滞超过 1m,但对于小型/正常大小的阵列,这是最干净的解决方案。
  • @MichaelWarner - 似乎速度上的差异几乎不明显,至少在 Chrome 60 中对我来说是这样 -> jsperf.com/duplicatenumber-array-js/1
  • 我完全同意。我写了一个脚本来对其中一些答案进行压力测试,你的答案在 3 秒内达到了 100 万,所以速度很快!然而之后就停滞不前了。这就是为什么我说它非常适合小型/正常大小的数据集。
  • @MichaelWarner - 你能发布那个“压力测试”吗?
  • 当然。这并不是真正的“压力测试”,您传入异常大的数组,并告诉我们数组方法比保留地图要慢,这并不令人惊讶。请注意,.random 会生成像 0.49943707635064727 和一千万个这样的浮点数,并且您开始遇到内存问题并减慢像 indexOf 这样的方法,因为数组突然占用了几百兆字节。在现实生活中这不是问题,jsPerf 可能更适合测试不同解决方案的速度。
【解决方案2】:

您可以在结束之前迭代直到元素并检查从 i + 1 直到结束。

这个函数只返回第一个副本。

带有一个循环和一个哈希表的快速版本。

function duplicateNumber(array) {
    var hash = Object.create(null),
        i, l, value;

    for (i = 0, l = array.length; i < l; i++) {
        value = array[i];
        if (hash[value]) {
            return value;
        }
        hash[value] = true;
    }
}

console.log(duplicateNumber([3, 5, 6, 8, 5, 3]));       // 5
console.log(duplicateNumber([0, 1, 2, 3, 4, 4, 5]));    // 4
console.log(duplicateNumber([0, 1, 1, 2, 3, 4, 4, 5])); // 1

【讨论】:

  • 对于 [0, 1, 1, 2, 3, 4, 4, 5] 失败
  • @NickA,第一个提案只返回第一个。
  • @NinaScholz 谢谢,我在评论并删除评论后立即看到它:)
  • @NickA,也许您喜欢对已访问项目使用不同的方法和哈希表。然后你只需要一个循环,如果发现一个骗子就退出。
  • 这对于大多数甚至无法达到 10m 的大型阵列非常有用 n = 100 time2: 0.197ms n = 1,000,000 time6: 1430.918ms n = 10,000,000 time7: 15726.190ms
【解决方案3】:

您的内部循环向下遍历到0。相反,它应该只下降到i+1,这样它就不会遇到外部循环中的当前字符,从而使其认为找到了重复项。

此外,您的内部循环应该从 arr.length-1 开始是正确的,这样它就不会测试越界索引。

代码已更新以反映问题的变化

function duplicateNumber(arr) {
  var idx = arr.length;

  OUTER:
  for (var i = 0; i < idx; i++) {
    for (var j = idx-1; j >= i+1; j--) {
      if (arr[i] === arr[j]) {
        idx = j
        continue OUTER
      }
    }
  }
  return arr[idx]
}

console.log(duplicateNumber([3, 5, 6, 8, 5, 3]));

当找到重复项时我也立即返回,因为没有理由继续循环应该在该点停止,这样你就不会用以后的重复项覆盖你的结果。

如果没有找到重复,则返回undefined


随着更新的需求,我们存储重复索引的索引,并继续。如果找到另一个副本,它将替换当前存储的副本。

内部循环总是从最后找到的索引之前的索引开始,向下遍历到i+1,因为我们不关心最后找到的索引之上的索引。

【讨论】:

  • 不是因为没有理由,如果它继续它可能(如果有的话)返回一个以后的副本!
  • 嘿,这行得通,但我误读了这个问题。该函数应返回未排序数组中的第一个重复值。所以在这样的数组中: [3, 5, 6, 8, 5, 3] 返回值应该是 5,而不是 3。
  • @user7496931:是的,就是这样做的。
  • 等等,为什么要返回5?第一个重复的成员是该未排序数组中的3
  • 数组中:[3, 5, 6, 8, 5, 3] 5是重复的before 3。所以返回值应该是5,而不是3。
【解决方案4】:

您不需要第二个循环,您可以使用Array.prototype.indexOf()。此外,您可以减少代码行并立即返回结果。例如:

function duplicateNumber(arr) {
  for (var i = 0; i < arr.length; i++) {
    if (arr.indexOf(arr[i]) !== i) {
      return arr[i];
    }
  }
  return null;
}

console.log(duplicateNumber([3, 5, 6, 8, 5, 3]));

【讨论】:

    【解决方案5】:
    function getFirstDupe(arr){
      var dupe;
    
      if(arr && arr.forEach){
        arr.forEach(function(item, index){
          if(dupe === undefined && arr.indexOf(item) !== index){
            dupe = item;
          }
        })
      }
    
      return dupe;
    }
    

    【讨论】:

    • 请提供解释以及仅发布代码。您的解决方案可能是正确的,但其他读者会对这种方法感兴趣
    【解决方案6】:

    只是另一种方法,您可以使用对象来存储计数

    function duplicateNumber(arr) {
        let count = {};
        for (var i = 0; i < arr.length; i++) {
            count[arr[i]] = (count[arr[i]] || 0) + 1;
            if(count[arr[i]] > 1){
                return arr[i];
            }
        }
        return dup_num
    }
    
    console.log(duplicateNumber([0, 1, 1, 2, 3, 4, 4, 5]));

    【讨论】:

      【解决方案7】:

      如果您希望在最短的时间内完成此操作。我会建议这样的事情。这将在最坏的情况下完成 O(n);

      function duplicateNumber(arr) {
        var memory = {};
        for (var i = 0; i < arr.length; i++) {
          if(memory[arr[i]] === undefined){
            memory[arr[i]] = true;
          }else{
            return arr[i]
          }
        }
      }
      
      var arr = [2,3,2,3];
      console.log(duplicateNumber(arr));

      【讨论】:

      【解决方案8】:

      详细说明 spanky 所说的:使用您的解决方案,您将在某一时刻总是将 arr[i] 与 arr[j] 进行比较,而 i 等于 j(因此等于它本身)。而且因为您在找到匹配项时不会停止迭代,所以我的猜测是您的函数将始终返回输入数组的最后一个元素。

      【讨论】:

        猜你喜欢
        • 2014-10-31
        • 2019-09-06
        • 2013-10-14
        • 1970-01-01
        • 2018-04-06
        • 1970-01-01
        • 2012-10-26
        • 2017-03-04
        • 1970-01-01
        相关资源
        最近更新 更多