找到总和为给定数字的唯一数字对的所有索引的最小总和

时间:2015-09-16 12:44:56

标签: javascript arrays algorithm

我想遍历一个数组,然后将每个值相互添加(除了自身+本身),如果循环的两个值之和等于我函数中的第二个参数,之前没有遇到过这对价值,然后记住他们的指数,并最后返回所有记忆指数的全部总和。

换句话说,问题陈述是:给定一个整数的数组 A ,第二个值 s 是一个所需的总和,从数组中查找所有值对索引 i j A i< j A [i] + A [j] = s ,并返回这些对的所有索引的总和,具有以下限制:

  • 不重用价值对,即如果两个指数对 i j k l 满足上述条件,如果 A [i] == A [k] A [j] == A [l] A [i ] == A [l] A [j] == A [k] ,然后忽略具有较高索引总和的货币对。

示例

例如,functionName([1, 4, 2, 3, 0, 5], 7)应该返回11,因为值4,2,3和5可以相互配对等于7,而11来自添加它们的索引到达11其中:

4 + 3 = 7 
5 + 2 = 7 

4 [index: 1]
2 [index: 2]
3 [index: 3]
5 [index: 5]

1 + 2 + 3 + 5 = 11

示例#2

functionName([1, 3, 2, 4], 4)只会等于1,因为只有前两个元素可以配对等于4,第一个元素的索引为0,第二个元素的索引为

1 + 3 = 4

1 [index: 0]
3 [index: 1]

0 + 1 = 1

这是我到目前为止所做的:

function functionName(arr, arg) {
    var newArr = [];
    for(var i = 0; i < arr.length; i++){
        for(var j = i + 1; j < arr.length; j++) {
            if((arr[i] + arr[j]) === arg ) {
                newArr.push(i , j);
            }
        }
    }

    if(newArr.length === 0) {
        return console.log(0);
    }
    return console.log(newArr.reduce(function(a,b){return a + b}));
}

functionName([1, 4, 2, 3, 0, 5], 7);

我遇到的问题是它一切正常但我有一个问题,一旦找到一对等于第二个参数,那么它不应该再次使用相同的值对,但我的确如此:

如果数组是[1,1,1]而第二个参数是2,循环将通过并找到答案,但它会在找到总和后继续搜索,我只希望它使用配对[1, 1]一次,所以如果它在索引[0, 1]找到这样的一对,那么它不应该包含任何其他包含值1的对。

我想我可以删除剩余的相同值,如果使用过滤器发现超过2个,如果有一个数组则只留下2个相同的值,因此不必担心循环找到1 + 1两次,但这是最好的方式去做吗?

我还是新手,但期待你的评论

PS 我打算使用纯JavaScript而不使用库

链接到JS小提琴,可能会让事情变得更容易看到我拥有的东西。 https://jsfiddle.net/ToreanJoel/xmumv3qt/

4 个答案:

答案 0 :(得分:1)

这比最初看起来更复杂。实际上,在循环内部进行循环会导致算法在数组大小方面具有quadratic time complexity。换句话说,对于大型数字数组,需要很长时间才能完成。

处理这个问题的另一种方法是注意你实际上只需要使用数组中的每个唯一值一次(或两次,如果 s 是偶数,你有两个 s / 2 值在数组中的某个位置)。否则,您将拥有非唯一对。这是有效的,因为如果你需要 x y 这样的数字,那么 x + y = s ,如果你知道 x ,然后确定 y - 它必须等于 s - x

因此,您可以在linear time complexity中实际解决问题(公平地说,如果 A 中的所有值都是唯一的,有时会n*log(n),因为我们有对它们进行一次排序)。

算法的步骤如下:

  1. 制作一个地图,其键是数组 A 中的值,值是按照 A 中显示的索引的排序列表。
  2. 按升序顺序浏览 A 中的所有唯一值(在解决步骤1时收集它们)。对于每个这样的值:
    1. 假设它是搜索到的值对的较低值。
    2. 计算较高的值(它等于s - lower
    3. 检查 A 中是否还存在较高的值(由于步骤1中创建的地图,您将在固定时间内执行此操作)。
    4. 如果是,请将较低和较高值的最低索引添加到结果中。
  3. 返回结果。
  4. 这里是完整的代码:

    &#13;
    &#13;
    function findSumOfUniquePairs(numbers, sum) {
    
      // First, make a map from values to lists of indexes with this value:
      var indexesByValue = {},
          values = [];
      numbers.forEach(function (value, index) {
        var indexes = indexesByValue[value];
        if (!indexes) {
          indexes = indexesByValue[value] = [];
          values.push(value);
        }
        indexes.push(index);
      });
    
      values.sort();
    
      var result = 0;
    
      for (var i = 0, maxI = values.length; i < maxI; ++i) {
        var lowerValue = values[i],
            higherValue = sum - lowerValue;
        if (lowerValue > higherValue) {
          // We don't have to check symmetrical situations, so let's quit early:
          break;
        }
        var lowerValueIndexes = indexesByValue[lowerValue];
        if (lowerValue === higherValue) {
          if (lowerValueIndexes.length >= 2) {
            result += lowerValueIndexes[0] + lowerValueIndexes[1];
          }
        } else {
          var higherValueIndexes = indexesByValue[higherValue];
          if (higherValueIndexes) {
            result += lowerValueIndexes[0] + higherValueIndexes[0];
          }
        }
      }
    
      return result;
    }
    
    document.write(findSumOfUniquePairs([1, 4, 2, 3, 0, 5], 7) + '<br>'); // 11;
    document.write(findSumOfUniquePairs([1, 3, 2, 4], 4) + '<br>'); // 1
    document.write(findSumOfUniquePairs([1, 1, 1], 2) + '<br>'); // 1
    document.write(findSumOfUniquePairs([1, 1, 1, 1], 2) + '<br>'); // 1
    document.write(findSumOfUniquePairs([1, 2, 3, 1, 2, 3, 1], 4) + '<br>'); // 7
    document.write(findSumOfUniquePairs([5, 5, 1, 1, 1], 6) + '<br>'); // 2
    document.write(findSumOfUniquePairs([0, 5, 0, 5, 1, 1, 1], 6) + '<br>'); // 5
    &#13;
    &#13;
    &#13;

答案 1 :(得分:0)

这样可行,但它会破坏初始数组。

function functionName(arr, arg) {
    var newArr = [];
    for(var i = 0; i < arr.length; i++){
        for(var j = i + 1; j < arr.length; j++) {
            if((arr[i] + arr[j]) === arg ) {
                newArr.push(i , j);
                arr[i] = null;
                arr[j] = null;
            }
        }
    }

    if(newArr.length === 0) {
        return console.log(0);
    }
    return console.log(newArr.reduce(function(a,b){return a + b}));
}

答案 2 :(得分:0)

如果找到总和,则使用重启循环的解决方案。找到的加数存储在usedNumbers中,然后排序并用于获取索引求和的索引。

排序和最后indexArray.prototype.indexOf提供了正确的起始位置。

编辑:

  

那么[1,1,1,1],2 ......应该是6还是1? - Jaromanda X 21

     

@JaromandaX,应该是1,在找到值对后,它不应该再找一个具有相同值的对 - Torean

此版本负责此要求。

function f(array, sum) {
    var arrayCopy = array.slice(0),
        usedNumbers = [],
        index = 0,
        indexA = 0,
        indexB, 
        a, b;

    while (indexA < arrayCopy.length) {
        indexB = indexA + 1;
        while (indexB < arrayCopy.length) {
            a = arrayCopy[indexA];
            b = arrayCopy[indexB];
            if (a + b === sum) {
                usedNumbers.push(a, b);
                arrayCopy = arrayCopy.filter(function (i) { return a !== i && b !== i; });
                indexA--; // correction to keep the index
                break;
            }
            indexB++;
        }
        indexA++;
    }
    return usedNumbers.sort().reduce(function (r, a, i) {
        index = array.indexOf(a, i === 0 || a !== usedNumbers[i - 1] ? 0 : index + 1);
        return r + index;
    }, 0);
}

document.write(f([1, 4, 2, 3, 0, 5], 7) + '<br>'); // 11
document.write(f([1, 1, 1], 2) + '<br>'); // 1
document.write(f([5, 5, 1, 1, 1], 6) + '<br>'); // 2
document.write(f([0, 5, 0, 5, 1, 1, 1], 6) + '<br>'); // 5
document.write(f([1, 1, 1, 1], 2) + '<br>'); // 1

答案 3 :(得分:0)

以下解决方案非常紧凑。它仅通过相关元素避免不必要的检查和循环。您可以在此处查看工作代码集: http://codepen.io/PiotrBerebecki/pen/RRGaBZ

function pairwise(arr, arg) {
  var sum = 0;
  for (var i=0; i<arr.length-1; i++) {
    for (var j=i+1; j<arr.length; j++) {
      if (arr[i] <= arg && arr[j] <= arg && arr[i] + arr[j] == arg) {
        sum += i+j; 
        arr[i] = arr[j] = NaN;
      }   
    }
  }
  return sum;
}

console.log(  pairwise([1, 1, 0, 2], 2)  ) // should return 6

引擎盖下:

  1. 从索引(i)= 0。
  2. 的元素开始循环
  3. 仅为稍后在数组中的元素添加第二个循环。他们的索引j始终高于i,因为我们将{1}添加到了i
  4. 如果两个元素(数字)都小于或等于arg,请检查它们的总和是否等于arg。如果其中一个数字大于arg,则可以避免检查总和。
  5. 如果找到了该对,则将其值更改为NaN,以避免进一步检查和复制。