【发布时间】:2022-10-17 23:51:53
【问题描述】:
我在 React 中构建了一个词库应用程序,它从网络词典 API 获取数据,并在用户搜索单词时将定义、同义词和反义词呈现为可折叠列表。我想添加一个功能来显示所有有效的搜索的单词的字谜(但这不是现在的问题)。
我已经编写了一个递归算法,它根据该单词的可能排列数来查找任何给定输入的所有可能排列。唯一的问题是,当输入长度超过 6 个字母时,我遇到了 RangeError。我知道我的算法能够和将要查找长度大于 6 个字符的输入的所有排列,但受到调用堆栈的最大大小的阻碍。
我尝试使用多种不同的非递归算法,这些算法从我偶然发现的各种其他来源中实现相同的目的,但除了一个之外,所有人都遇到了同样的问题。但是,如果可能的话,我想重构我的解决方案以使其可行,而不是复制我找到的一个可行的解决方案。我将展示我的解决方案和工作解决方案以供参考。
我的解决方案:
/* These next two helper functions can be ignored, I've included them in case
of your curiosity. However, they are unimportant to the problem at hand.
Both functions merely determine the total number of possible permutations for a given
input, which I use to determine how many times my final function should recurse */
// Helper function 1
const hasDuplicates = (str) => {
const letters = {};
str.split('').forEach(letter => {
if (letters[letter] !== undefined) letters[letter]++;
if (letters[letter] === undefined) letters[letter] = 1;
});
for (let key in letters) {
let currLetter = letters[key];
if (currLetter > 1) return letters;
};
return false;
};
// Helper function 2
const numPermutations = (str) => {
if (hasDuplicates(str) === false) {
let multiplier = 1;
for (let i = 1; i <= str.length; i++) multiplier *= i;
return multiplier;
};
const letters = hasDuplicates(str);
let multiplier = 1;
let divisor = 1;
let visited = new Set();
for (let i = 1; i <= str.length; i++) {
let currLetter = str[i];
if (letters[currLetter] > 1 && !visited.has(currLetter)) {
for (let j = 1; j <= letters[currLetter]; j++) {
divisor *= j;
};
visited.add(currLetter);
};
multiplier *= i;
};
return (multiplier / divisor);
};
// Final recursive function
const permutations = (string, finalArray = [], i = 0, visited = new Set()) => {
/* If the input consists of only two values and both are identical, we know that
further evaluation is unnecessary. */
if (string.length === 2) {
if (string.split('')[0] === string.split('')[1]) {
finalArray.push(string);
return finalArray;
};
};
if (string.length <= 2 && finalArray.length === string.length) return finalArray;
// Call to previous helper function which determines number of times we must recurse
const maxPermutations = numPermutations(string);
if (i === maxPermutations) return finalArray;
const splitString = string.split('');
// Scramble the letters of the string and rearrange them in a random order
for (let i = splitString.length - 1; i > 0; i--) {
let randNum = Math.floor(Math.random() * (i + 1));
let replacement = splitString[i];
splitString[i] = splitString[randNum];
splitString[randNum] = replacement;
};
if (!visited.has(splitString.join(''))) {
/* If we don't already have this random variation of the string in our array,
push it into our final array, add it to the set of strings we've encountered,
and increment our index variable to work towards the base case */
finalArray.push(splitString.join(''));
visited.add(splitString.join(''));
return permutations(string, finalArray, i += 1, visited);
};
/* If we have encountered the latest random variation of our string,
recurse without incrementing our index (does not work toward base case) */
return permutations(string, finalArray, i, visited);
};
同样,这对于长度少于 7 个字符的输入非常有效。但是,任何更长的时间,都会超出最大调用堆栈大小。我在下面包含了我发现的解决此问题的一个解决方案,希望它能够阐明我的解决方案的可能解决方法。话虽这么说,我不明白这个解决方案是如何工作的或为什么工作,只是它确实如此。我将在我的应用程序中使用它作为最后的手段,但我更喜欢使用我自己的工作而不是其他人的工作。
function permutes(string) {
var s = string.split('').sort();
var res = [s.join('')]
while(true) {
var j = s.length - 2;
while (j != -1 && s[j] >= s[j + 1])
j--;
if(j == -1)
break;
var k = s.length - 1;
while(s[j] >= s[k])
k--;
[s[j], s[k]] = [s[k], s[j]];
var l = j + 1, r = s.length - 1;
while (l<r) {
[s[l], s[r]] = [s[r], s[l]];
l++;
r--;
}
res.push(s.join(''));
}
return res;
}
【问题讨论】:
标签: javascript recursion permutation infinite-loop callstack