我编写了以下算法来完成一个单词阶梯。您可以在我的github上找到包含jQuery可视化和json字典的完整程序:https://github.com/NateBrady23/algorithms/tree/master/word_ladder
所以我很少知道我正在使用的算法的名称是什么,以及我正在构建的数据结构的名称,而不是我用每个级别的节点填充一些数组。
我从给定的输入词和结束词开始。我删除了字典中我无法使用的所有单词(输入单词和单词长度与输入单词的长度不同)。我构建了一个Node对象,它保存了它所在的跳转级别,一个值和一个prev,所以如果我找到一个路径,我可以从我的端点向后遍历。
从我的输入词0级开始,我找到所有可以通过转换一个字母并将其保存到1级wordNodes桶中的单词创建的单词。每个Node对象的prev属性都指向它所找到的0级节点。然后我从字典中删除所有这些单词。然后我浏览所有1级单词节点,并为每个单词找到修改后的字典中的转换后的单词并将其存储在2级并指向创建它的单词,依此类推,直到找到目标单词或没有新级别可以创建(没有我的话的路径)。
所以考虑到可能措辞不好的摘要,我建立的这种类型的结构是否有名称(如二叉搜索树?)以及我用来实现的算法。另外,有没有办法在不使用完全不同的结构/算法的情况下显着改进下面的代码。
<style>
span {
margin: 10px;
}
.highlight {
background-color: yellow;
font-weight: bold;
}
span {
display: inline-block;
}
</style>
<div id="path">
</div>
<script src='js/jquery.min.js'></script>
<script src='js/dictionary.js'></script>
<script>
// var dict = JSON.parse('{"aa" : true, "aah" : true, "aahed" : true}');
// Build our dictionary array
// Create a Node object so we can traverse back up the tree
function Node(value, prev, level) {
this.prev = prev;
this.level = level;
this.value = value;
}
function getRegex(word) {
var temp = [];
for(var i = 0; i < word.length; i++){
temp.push( word.substring(0,i) + '.' + word.substring(i+1) );
}
var re_str = '^(' + temp.join('|') + ')$';
return new RegExp(re_str, 'i');
}
function oneOffs(wordNode) {
var list = [];
var regex = getRegex(wordNode.value);
if ($('#' + (wordNode.level+1)).length == 0) {
$('#path').append('<div id="' + (wordNode.level+1) + '"></div>');
$('#' + (wordNode.level+1)).append('<h1>Level: ' + (wordNode.level+1));
}
for (var word in dict) {
// If we found a match
if (dict.hasOwnProperty(word) && regex.test(word)) {
list.push(new Node(word, wordNode, wordNode.level + 1));
$('#' + (wordNode.level+1)).append('<span id="' + word + '">' + word +'</span>');
delete dict[word];
}
}
return list;
}
// Get our start and end words
inputWord = 'boggle';
endWord = 'shaggy';
$('#path').append('<div id="start"><h1>Transform Case</h1><span><b>Starting Word:</b> ' + inputWord + '</span><span><b>Ending Word:</b> ' + endWord + '</span></div>');
$('#path').append('<div id="0"><h1>Level: 0</h1><span id="' + inputWord + '">' + inputWord + '</span></div>');
// Have to remove our inputWord from the dictionary
delete dict[inputWord];
// Remove all words in dictionary not of proper length
for (var word in dict) {
if (dict.hasOwnProperty(word)) {
if (word.length != inputWord.length) {
delete dict[word];
}
}
}
// Create input and end nodes
inputNode = new Node(inputWord, null, 0);
endNode = new Node(endWord, null, 0);
// Create our initial list of level 1 nodes
list = oneOffs(inputNode);
for (var node in list) {
if (list[node].value === endNode.value) {
endNode.level = list[node].level;
endNode.prev = list[node].prev;
}
}
// Build our tree
while (list.length && !endNode.level) {
newList = [];
for (var i = 0; i < list.length; i++) {
newNodeList = oneOffs(list[i]);
for (var node in newNodeList) {
if (newNodeList[node].value === endNode.value) {
endNode.level = newNodeList[node].level;
endNode.prev = newNodeList[node].prev;
break;
}
}
if (newNodeList.length) {
newList = newList.concat(newNodeList);
}
}
list = newList;
}
// if we found the path, let's traverse back up and print out all the values
if (endNode.level) {
curr = endNode;
$('#' + curr.value).addClass('highlight');
console.log(curr.value);
while(curr.prev) {
$('#' + curr.prev.value).addClass('highlight');
console.log(curr.prev.value);
curr = curr.prev;
}
} else {
console.log('No possible path');
}
</script>
答案 0 :(得分:3)
评论指出这是广度优先搜索,但我不知道此算法的任何特定名称。当然,您的数据结构是某种树。
至于优化,我没有仔细查看代码,但有一件事情在我身上飞跃。要从一个步骤移动到另一个步骤,您需要构造一个复杂的正则表达式来匹配特定级别的每个单词,然后遍历整个字典以查看其中的任何单词是否与此正则表达式匹配。构建实际可能的单词并检查每个单词是否在字典中肯定会更有效。也就是说,如果您有foo
,请检查您的字典aoo
,boo
,coo
,... zoo
,fao
,{ {1}},fbo
,... fco
,fzo
,foa
,fob
,... foc
。由于您已经从字典中删除了访问过的字词,因此您无法在此列表中获得foz
的多个新匹配。
这应该会显着减少您必须进行的检查次数,并且会从正则表达式测试更改为简单的属性查找。我猜测它会大大提升性能。