在真实的Javascript编码测试中如何避免嵌套循环

时间:2019-05-28 03:28:38

标签: javascript loops nested

强文本在两个输入数组之间,找到相同的一个或多个数组并返回它们。

我只是想出了嵌套循环解决方案。


const compareArrs = (arr1, arr2) => {
  const rtnArr = []
  for (let ar1 of arr1){
    for (let ar2 of arr2){
      if (JSON.stringify(ar1) === JSON.stringify(ar2)){
        rtnArr.push(ar1)
      }
    }
  }
  return rtnArr
}

// compare [1, 2] [3, 4] with [1, 2]
console.log(compareArrs([[1, 2], [3, 4]], [[1, 2]]))

[[1,2]]应该返回

3 个答案:

答案 0 :(得分:1)

映射一个参数的每个子数组并将其放入Set中。与另一个参数一起迭代遍历,随即进行字符串化处理,并在集合中找到已字符串化数组的元素,总体复杂度为O(N)

const compareArrs = (arr1, arr2) => {
  const arr1StringifiedSet = new Set(arr1.map(JSON.stringify));
  return arr2.find(
    subarr2 => arr1StringifiedSet.has(JSON.stringify(subarr2))
  );
};

console.log(compareArrs([[1, 2], [3, 4]], [[1, 2]]));

(相比之下,嵌套循环的复杂度为O(N^2)

如注释所示,如果您实际上需要 all 个匹配数组,而不仅仅是第一个,则使用.filter而不是.find

const compareArrs = (arr1, arr2) => {
  const arr1StringifiedSet = new Set(arr1.map(JSON.stringify));
  return arr2.filter(
    subarr2 => arr1StringifiedSet.has(JSON.stringify(subarr2))
  );
};

console.log(compareArrs([[1, 2], [3, 4]], [[1, 2]]));

答案 1 :(得分:1)

您可以模拟一组带有嵌套映射的数组,其中[1,2]成为根映射中的项,键为1,值是另一个具有键2的映射,如下所示:

const present = Symbol('present');

class ArraySet {
    constructor() {
        this._root = new Map();
    }

    add(array) {
        let node = this._root;

        for (const item of array) {
            if (node.has(item)) {
                node = node.get(item);
            } else {
                node.set(item, node = new Map());
            }
        }

        node[present] = true;
    }

    has(array) {
        let node = this._root;

        for (const item of array) {
            node = node.get(item);

            if (node === undefined) {
                return false;
            }
        }

        return !!node[present];
    }
}

然后:

const compareArrs = (arr1, arr2) => {
    const set = new ArraySet();
    arr1.forEach(set.add, set);
    return arr2.filter(set.has, set);
};

const present = Symbol('present');

class ArraySet {
    constructor() {
        this._root = new Map();
    }

    add(array) {
        let node = this._root;

        for (const item of array) {
            if (node.has(item)) {
                node = node.get(item);
            } else {
                node.set(item, node = new Map());
            }
        }

        node[present] = true;
    }

    has(array) {
        let node = this._root;

        for (const item of array) {
            node = node.get(item);

            if (node === undefined) {
                return false;
            }
        }

        return !!node[present];
    }
}

const compareArrs = (arr1, arr2) => {
    const set = new ArraySet();
    arr1.forEach(set.add, set);
    return arr2.filter(set.has, set);
};

console.log(compareArrs([[1, 2], [3, 4]], [[1, 2]]));

这花费与arr1.concat(arr2).flat().length成比例的时间。只要您可以为数组创建适当的比较函数,另一种方法就可以工作,例如这种字典法,适用于通常的JavaScript运算符给出总排序的情况:

const lexCompare = (a, b) => {
    const len = Math.min(a.length, b.length);

    for (let i = 0; i < len; i++) {
        if (a[i] < b[i]) return -1;
        if (a[i] > b[i]) return 1;
    }

    return a.length - b.length;
};

并且输入数组不包含重复项是先对两个数组的组合进行排序:

const sorted = arr1.concat(arr2).sort(lexCompare);

然后查找由合并而彼此相邻创建的重复项:

return sorted.filter((item, i) => {
    if (i === 0) return false;

    const prev = sorted[i - 1];
    return prev.length === item.length && prev.every((x, i) => x === item[i]);
});

时间O((| arr1 | + | arr2 |)log(| arr1 | + | arr2 |)l)其中l是最大数组长度。您可以将(| arr1 | + | arr2 |)日志(| arr1 | + | arr2 |)减少为| arr1 |日志| arr1 | + | arr2 |日志| arr2 |但最终可能会变慢。

答案 2 :(得分:1)

您可以将Array.prototype.reduce()Array.prototype.find()Array.prototype.push()组合使用

const compareArrs = (arr1, arr2) => arr1.reduce((a, c) => {
  const found = arr2.find(arr => JSON.stringify(arr) === JSON.stringify(c))
  if (found.length) a.push(found)
  return a
}, [])

// compare [1, 2] [3, 4] with [1, 2]
console.log(compareArrs([[1, 2]], [[1, 2], [3, 4]]))