【问题标题】:refactorized solve for an algorithm in javascriptjavascript中算法的重构求解
【发布时间】:2018-06-17 16:58:33
【问题描述】:

我参加了一家开发公司的技术面试。他们问了我以下问题:

给出一个数字数组 (n) 找到 2 个总和给出 (k) 的数字并打印出来。
例如

Input: n = [2,6,4,5,7,1], k = 8   
Output: result=(2,6),(7,1)   

我的解决方案:

function findSum(n,k){
    let aux = []
    for (let i = 0; i < n.length; i++) {
        for (let j = i+1; j < n.length; j++) {
            if (n[i] + n[j] == k) {
                aux.push({ first: n[i], second: n[j] })
            }
        }
    }
    return aux;
}

他们告诉我,可以使用某种键或映射来进行练习。
有人知道如何只用一个循环来做到这一点吗?

【问题讨论】:

  • 您正在检查所有对 O(n^2),您可以使用哈希表,插入所有元素将是 O(n),再次遍历将是 O(n),并进行恒定比较。 O(n + n) = O(n)
  • 插入一个元素成本O(log n),因此插入所有元素成本O(n * log n),整体复杂度为O(n * log n)
  • @VasilyLiaskovsky 你从哪里得到 O(log n)?将具有良好哈希函数的元素插入到表中不会花费 O(log n)。

标签: javascript arrays algorithm sorting


【解决方案1】:

以低时间复杂度解决此类问题的关键是高效搜索数据结构的能力。许多答案以优化搜索数组的方式重新排列数组。另一种方法是使用固有快速搜索的数据结构。

Set 和 Map 数据结构的搜索时间复杂度为 O(1),这使得它们成为可以利用搜索来提高性能的良好数据结构。

我使用new Map 并在将其添加为键的同时遍历数组。我将key 设置为数字,并将值设置为我看到它的次数。我在new Set 上使用地图,因为我还可以跟踪该特定数字的实例数。

我搜索总和为k 的数字,即:(k - num)。如果找到该数字,我会将这两个数字添加到我的结果数据结构中,并将 value 减 1,以表明它已被使用。

时间复杂度:O(n),内存复杂度:O(2n)。与原始数组相比,空间量是原来的两倍,因为我有一个 key 和一个 value 要存储在我的 Map

function pairSums(arr, k){
    const map = new Map
    const matches = []
    for (let num of arr) {
        const search = k - num 
        if (map.get(search) > 0) {
            matches.push([num, k - num])
            map.set(search, map.get(search) - 1)
        } else if (!map.has(num)){
            map.set(num, 1)
        } else {
            map.set(num, map.get(num) + 1)
        }
    }
    return matches
}

console.log(pairSums([2, 6, 6, 6, 2, 4, 4, 4, 5, 7, 1, 4, 2], 8))

【讨论】:

    【解决方案2】:

    将数组中的数字 x 与键 Math.min(x, k - x) 匹配。然后遍历您的数组并使用提到的键将每个数字存储在哈希中。当您要添加的键已经在哈希中时 - 检查存储的值和当前数字是否给出所需的总和。

    function findSum(n, k){
      let hash = {};
      for(let i = 0; i < n.length; ++i){
        let x = n[i], key = Math.min(x, k - x);
        if((key in hash) && hash[key] + x == k)
          return [hash[key], x];
        else hash[key] = x;
      }
    }
    

    【讨论】:

    • if 语句不做其他循环吗?(key in hash)
    • 根据预期的输出,看起来 OP 正在寻找正确求和的每个实例。不只是第一个。
    【解决方案3】:

    这样的任务可以很简单,也可以很复杂,只要你想完成。这是一种解决方案,例如:

    function findPairs(n, k) {
        return n.reduce((pairs, next, i) => pairs.concat(
            n.slice(i + 1)
                .filter(num => next + num === k)
                .map(num => [ next, num ])
            ),
            []
        );
    }
    

    对于输入 [2, 6, 4, 5, 7, 1]8 将输出 [ [2, 6], [7, 1] ]

    【讨论】:

    • 哦,我错过了最后一部分。去图吧。
    • 考虑循环数会产生误导,因为这些函数调用可能会嵌入“隐藏”循环。重要的是复杂性函数。
    • 是的,我认为这也是@DaveNewton 所指出的。我的解决方案中的所有函数调用都可能执行一个循环,因此时间复杂度要大得多。
    【解决方案4】:

    来自https://www.geeksforgeeks.org/write-a-c-program-that-given-a-set-a-of-n-numbers-and-another-number-x-determines-whether-or-not-there-exist-two-elements-in-s-whose-sum-is-exactly-x/

    1. 按非降序对数组进行排序。
    2. 初始化两个索引变量以查找排序数组中的候选元素。将l初始化到最左边的索引:l = 0,将r初始化到最右边的索引:r = n.length - 1
    3. l r 时循环。
      1. if (n[l] + n[r] == k) 然后return 1
      2. else if( n[l] + n[r] &lt; k ) 然后l++
      3. else r--
    4. 整个数组中没有候选人 - return 0

    【讨论】:

    • 不会将排序视为(至少)一个循环吗? (并且可能更长?)
    • 我想这取决于面试官来计算,但是是的。您可以使用此解决方案在 O(n log n) 中完成此操作,但上面已经发布了更好的解决方案
    【解决方案5】:

    我认为通过排序可以做到这一点

    var n = [2,6,4,5,7,1];
    var k = 8 ;
    n.sort();
    
    
    let start = 0, end = n.length-1;
    while(start < n.length && end >= 0) {
        let current_sum = (n[start] + n[end]);
        if(current_sum == k) {
            console.log('Found sum with '+ n[start] +' and '+ n[end]);
            break;
        }
        else if(current_sum > k) {
            end--;
        } else {
            start++;
        }
    }
    if(start == n.length || end < 0) {
        console.log('Not Found');
    }
    

    但在编写此代码时,我也有另一种方法

    const set = new Set([2,6,4,5,7,1]);
    var k = 8;
    let found = false;
    for (let item of set) {
        let another = k - item;
        if(set.has(another)){ 
            console.log('found with '+item +' and ' +another);
            found = true;   
            break;
        }
    }
    if(!found) {
        console.log('Not found');
    }
    

    【讨论】:

    • 如果数组中有重复的数字,在某些情况下使用集合会停止工作。
    【解决方案6】:

    如果数字为非负数且目标值在 JavaScript 数组限制内:

    function findsums(arr,k){
        var ret=[];
        var aux=[];
        arr.forEach(function(i){
          if(i<=k){
            if(aux[k-i])
              ret.push([k-i,i]);
            aux[i]=true;
          }
        });
        return ret;
    }
    
    console.log(findsums([2,6,4,5,7,1],8));

    类似的方法也适用于位域(甚至是稀疏的位域数组)。

    【讨论】:

      【解决方案7】:

      类似于@Andrew 的最佳答案的缩小替代方案,但假设所有数字都高于 0:

      var pairs = (arr, k) => arr.reduce((a, n) => 
        (a[n - k]-- ? a.push([n, k - n]) : a[-n] = a[-n] | 0 + 1, a), []);
      
      console.log(JSON.stringify( pairs([2,6,4,5,7,1], 8) ));

      【讨论】:

        猜你喜欢
        • 2013-04-16
        • 2019-11-28
        • 1970-01-01
        • 1970-01-01
        • 2016-01-26
        • 2012-08-22
        • 1970-01-01
        • 1970-01-01
        • 2023-03-12
        相关资源
        最近更新 更多