一个算法题
继续优化:存储输入参数2元素个数的map也可以引用传递,只需要在return false
之前把自己的修改还原就行了。减少的值加回去,就回溯到以前的状态了。
如果是return true
,那就不用还原了,因为答案已经找到了。
继续优化:
因为采用引用传递了,所以该算法也可以从递归改成迭代。只需要一个栈记录每层遍历到哪里了就行。递归其实就是在函数调用栈里记录了每层的遍历位置。
看着老虎说的,按照自己的理解写了一段代码,但是这有个大问题:生成全排列的复杂度是O(n^n)。
// 描述: 手里有一组数字卡片,每个人说出自己想要的一组数字(只能领到一张想要的卡片)。要判断手里的卡片是否能按大家需要分配每一个人。
function main(desire, gift) {
const combina = (arr1, arr2) => {
let arr = [];
for (let v1 of arr1) {
for (let v2 of arr2) {
arr.push(`${v1}${v2}`);
}
}
return arr;
};
const generate = (arr, length) => {
if (length <= 2) {
return combina(arr[length - 2], arr[length - 1]);
}
return combina(generate(arr, length - 1), arr[length - 1]);
};
const fullArr = generate(desire, desire.length);
return fullArr.some((item) => {
const ngift = [...gift];
const length = item.split("").length;
for (let i = 0; i < length; i++) {
const index = ngift.indexOf(Number(item[i]));
if (index === -1) break;
ngift.splice(index, 1);
if (i === length - 1) {
return true;
}
}
return false;
});
}
console.log(main([[1, 2], [1, 3], [3]], [1, 3, 4]));
红米K30 Pro(变焦版)
老虎也可能说的是这个意思: 回溯加剪枝
// 描述: 手里有一组数字卡片,每个人说出自己想要的一组数字(只能领到一张想要的卡片)。要判断手里的卡片是否能按大家需要分配每一个人。
function main(desire, gift) {
const path = [];
let status = false;
const dfs = (index) => {
if (index > desire.length - 1) {
console.log("递归完成:", path);
const ngift = [...gift];
for (let i = 0; i < path.length; i++) {
const index = ngift.indexOf(path[i]);
if (index === -1) return;
ngift.splice(index, 1);
if (i === path.length - 1) {
status = true;
}
}
return;
}
for (let item of desire[index]) {
path.push(item);
console.log("递归前:", path);
index++;
dfs(index);
if (status) {
return;
}
path.pop();
index--;
console.log("递归后:", path);
}
};
dfs(0);
return status;
}
console.log(main([[1, 2], [1, 3], [3]], [1, 3, 3]));
红米K30 Pro(变焦版)
提前设置好map,就不需要每次使用indexOf去判断了。
// 描述: 手里有一组数字卡片,每个人说出自己想要的一组数字(只能领到一张想要的卡片)。要判断手里的卡片是否能按大家需要分配每一个人。
function main(desire, gift) {
const path = [];
let status = false;
const map = {};
for (let item of gift) {
if (map[item]) {
map[item] = map[item] + 1;
} else {
map[item] = 1;
}
}
const dfs = (index) => {
if (status) {
return;
}
if (index > desire.length - 1) {
console.log("递归完成:", path);
const nmap = { ...map };
for (let i = 0; i < path.length; i++) {
if (nmap[path[i]] && nmap[path[i]] > 0) {
nmap[path[i]] = nmap[path[i]] - 1;
} else {
return;
}
}
status = true;
return;
}
for (let item of desire[index]) {
path.push(item);
console.log("递归前:", path);
index++;
dfs(index);
path.pop();
index--;
console.log("递归后:", path);
}
};
dfs(0);
return status;
}
console.log(main([[1, 2], [1, 3, 4], [3]], [1, 3, 3]));
红米K30 Pro(变焦版)
@老虎会游咏q,19楼的方法似乎就是“动态规划”🤣🤣🤣
也就是做了前缀优化的排列组合。
采用逐层遍历+递归的方法生成排列,存储输入参数2元素个数的map通过值传递传给下一层,然后每层都进行取数尝试,一但某层取的数不存在,就从递归里返回false,放弃整个分支,回到下一个分支继续尝试。
因为存储输入参数2元素个数的map是值传递,所以层与层互不影响,return就能无损回溯。