这里还有一个非常优雅的解决方案,没有循环或递归,只使用了map和reduce数组本地函数。
const getAllSubsets =
theArray => theArray.reduce(
(subsets, value) => subsets.concat(
subsets.map(set => [value,...set])
),
[[]]
);
console.log(getAllSubsets([1,2,3]));
reduce()
和map()
,而不是因为自己的误解而责怪一种非常合理且易懂的解决方案。 - dmuensterer我们可以解决输入数组的一个子集问题,从 offset
开始。然后我们递归回到原问题得到完整的解决方案。
使用生成器函数,可以在常量内存使用情况下迭代子集:
// Generate all array subsets:
function* subsets(array, offset = 0) {
while (offset < array.length) {
let first = array[offset++];
for (let subset of subsets(array, offset)) {
subset.push(first);
yield subset;
}
}
yield [];
}
// Example:
for (let subset of subsets([1, 2, 3])) {
console.log(subset);
}
运行时复杂度与解决方案数量(2ⁿ)和平均每个解决方案的长度(n/2)成正比,即O(n2ⁿ)。
for (let subset of subsets(array))
的每次迭代的内存使用量受到array
长度的限制,假设为n而不是2^n,如果我们首先构建所有子集,然后再迭代它们,那么情况就会是这样。 - le_mO(n)
内存增长方面有所增加。根据我的性能测试,在生成器的第一次迭代(即.next()
),我们正在从0...n
实例化所有生成器函数。与尾递归以O(1)
的内存增长率增长不同,这似乎以O(n)
的速度增长...顺便说一句 - 我希望我的分析是错误的,因为生成器方法似乎是一种优雅的方法。(附注:我发现了一篇优质文章:https://medium.com/javascript-scene/7-surprising-things-i-learned-writing-a-fibonacci-generator-4886a5c87710) - Shaheen GhiassyO(1)
的内存增长率增长” - 内存复杂度不能是O(1)
,因为每个子集已经占用了O(n)
的空间。你说的“以O(n)
的内存增长率增加”是什么意思 - 总空间复杂度是O(n),其中n是数组长度? - le_m无需递归的简单解决方案:
function getAllSubsets(array) {
const subsets = [[]];
for (const el of array) {
const last = subsets.length-1;
for (let i = 0; i <= last; i++) {
subsets.push( [...subsets[i], el] );
}
}
return subsets;
}
它是如何工作的?
如果我们从输入数字生成了一些子集,并且希望向输入数组添加一个数字,这意味着我们可以获取所有已经存在的子集,并通过将新数字附加到每个现有子集来生成新的子集。
以下是[1, 2, 3]
的示例
从一个空的子集开始:[]
通过将“1”添加到每个现有子集中创建新的子集。结果为:[]
[1]
通过将“2”添加到每个现有子集中创建新的子集。结果为:[], [1]
[2], [1, 2]
通过将“3”添加到每个现有子集中创建新的子集。结果为:[], [1], [2], [1, 2]
[3], [1, 3], [2, 3], [1, 2, 3]
subsets.slice(1)
。 - icosminfunction getCombinations(array) {
function fork(i, t) {
if (i === array.length) {
result.push(t);
return;
}
fork(i + 1, t.concat([array[i]]));
fork(i + 1, t);
}
var result = [];
fork(0, []);
return result;
}
var data = [1, 2, 3],
result = getCombinations(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
.as-console-wrapper
的目的是什么? - le_m您可以轻松地从数组生成幂集,类似以下方式:
var arr = [1, 2, 3];
function generatePowerSet(array) {
var result = [];
result.push([]);
for (var i = 1; i < (1 << array.length); i++) {
var subset = [];
for (var j = 0; j < array.length; j++)
if (i & (1 << j))
subset.push(array[j]);
result.push(subset);
}
return result;
}
console.log(generatePowerSet(arr));
在函数的主循环中,创建子集然后将其推入result
数组。
result.push(subset)
移出循环增量,放到循环体中可能会提高可读性。如果您已经转向使用位运算:Math.pow(2, j) == 1 << j
- le_mconst PowerSet = array => {
const result = [[]] // Starting with empty set
for (let value of array) { // For each value of the array
const length = result.length // Can't use result.length in loop since
// results length is increased in loop
for (let i = 0; i < length; i++){
let temp = result[i].slice(0) // Make a clone of the value at index i
temp.push(value) // Add current value to clone
result.push(temp) // Add clone back to results array
}
}
return result;
}
console.log(PowerSet([1,2,3]))
const powerSetRecursive = (arr, prefix=[], set=[[]]) => {
if(arr.length === 0) return// Base case, end recursion
for (let i = 0; i < arr.length; i++) {
set.push(prefix.concat(arr[i]))// If a prefix comes through, concatenate value
powerSetRecursive(arr.slice(i + 1), prefix.concat(arr[i]), set)
// Call function recursively removing values at or before i and adding
// value at i to prefix
}
return set
}
console.log(powerSetRecursive([1,2,3]))
function subSets(num){
/*
example given number : [1,3]
[]
1: copy push 1
[] [1]
3: copy push 3
[] [1] [3] [1,3]
*/
let result = [];
result.push([]);
for(let i=0; i < num.length;i++){
let currentNum = num[i];
let len = result.length;
for(let j=0; j < len; j++){
let cloneData = result[j].slice();
cloneData.push(currentNum);
result.push(cloneData)
}
}
return result;
}
let test = [1,3];
console.log(subSets(test))//[ [], [ 1 ], [ 3 ], [ 1, 3 ] ]
使用 reduceRight 方法:
const subsets = array =>
array.reduceRight(
(accumulator, a) => [...accumulator, ...accumulator.map(b => [a, ...b])],
[[]]
);
console.log(subsets([1, 2, 3])); // [[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
var getAllSubsets = (nums) => {
const subsets = [[]];
for (n of nums) {
subsets.map((el) => {
subsets.push([...el, n]);
});
}
return subsets;
};
console.log(getAllSubsets([1, 2, 3]));
// [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
For循环:
function powerSet(numbers) {
const subsets = [[]]
for (const number of numbers) {
subsets.forEach(subset => subsets.push([...subset, number]))
}
return subsets
}
function powerSet(numbers) {
const subsets = [[]]
if (numbers.length === 0) return subsets
for (let i = 0; i < numbers.length; i++) {
subsets.push(...powerSet(numbers.slice(i + 1)).map(subset => [numbers[i], ...subset]))
// Or step by step:
// const number = numbers[i]
// const otherNumbers = numbers.slice(i + 1)
// const otherNumbersSubsets = powerSet(otherNumbers)
// const otherNumbersSubsetsWithNumber = otherNumbersSubsets.map(subset => [number, ...subset])
// subsets.push(...otherNumbersSubsetsWithNumber)
}
return subsets
}
[1, 1]
或者[1, 2, 1]
的输出会是什么? - ibrahim mahrir[1, 1]
的幂集可能是[], [1], [1], [1, 1]
- 但如果您有更好的想法,请发表。@ibrahimmahrir - le_m