生成长度为n的子集

时间:2016-06-18 02:37:20

标签: javascript set subset

给定Set,生成长度为n的所有子集。

  • 示例输入:

    set = new Set(['a', 'b', 'c']), length = 2
    
  • 示例输出:

    Set {'a', 'b'}, Set {'a', 'c'}, Set {'b', 'c'}
    

如何有效地执行此操作,而不将Set转换为Array

我已经为数组输入了一个很好的解决方案:



function* subsets(array, length, start = 0) {
  if (start >= array.length || length < 1) {
    yield new Set();
  } else {
    while (start <= array.length - length) {
      let first = array[start];
      for (subset of subsets(array, length - 1, start + 1)) {
        subset.add(first);
        yield subset;
      }
      ++start;
    }
  }
}

let array = ['a', 'b', 'c', 'd'];

for (subset of subsets(array, 2)) {
  console.log(...subset);
}
&#13;
&#13;
&#13;

然而,我无法为集合提供有效的实现,而无需将集合转换为数组 - 我希望避免使用例如纯粹使用迭代器。

1 个答案:

答案 0 :(得分:1)

我实现了删除和重新插入有序多子集的值的想法:

function orderedMultiSubsets(set, n) {
  if(!Number.isInteger(n) || n < 0) return function*(){}();
  var subset = new Array(n),
      iterator = set.values();
  return (function* backtrack(index) {
    if(index === n) {
      yield subset.slice();
    } else {
      for(var i=0; i<set.size; ++i) {
        subset[index] = iterator.next().value; /* Get first item */
        set.delete(subset[index]); /* Remove it */
        set.add(subset[index]); /* Insert it at the end */
        yield* backtrack(index+1);
      }
    }
  })(0);
}
for(var subset of orderedMultiSubsets(new Set([1,2,3]), 2)) {
  console.log(subset.join());
}

然后我想我尽可能早地修剪了无序子集的情况:

function subsets(set, n) {
  if(!Number.isInteger(n) || n < 0 || n > set.size) return function*(){}();
  var subset = new Array(n),
      iterator = set.values();
  return (function* backtrack(index, remaining) {
    if(index === n) {
      yield subset.slice();
    } else {
      for(var i=0; i<set.size; ++i) {
        subset[index] = iterator.next().value; /* Get first item */
        set.delete(subset[index]); /* Remove it */
        set.add(subset[index]); /* Insert it at the end */
        if(i <= remaining) {
          yield* backtrack(index+1, remaining-i);
        }
      }
    }
  })(0, set.size-n);
}
for(var subset of subsets(new Set([1,2,3,4]), 2)) {
  console.log(subset.join());
}

我仍然使用数组作为子集,但其大小仅为n。因此,如果集合的大小比n大得多,这种方法可能会使用比将数据集复制到数组更少的内存,我想这是你的问题的重点。但请注意,删除和插入可能比数组查找更昂贵。

奖励:最后,集合的顺序与调用函数之前的顺序相同。