Word Ladder,Javascript:我使用的算法和数据结构是什么?

时间:2015-09-11 17:44:30

标签: javascript algorithm

我编写了以下算法来完成一个单词阶梯。您可以在我的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>

1 个答案:

答案 0 :(得分:3)

评论指出这是广度优先搜索,但我不知道此算法的任何特定名称。当然,您的数据结构是某种树。

至于优化,我没有仔细查看代码,但有一件事情在我身上飞跃。要从一个步骤移动到另一个步骤,您需要构造一个复杂的正则表达式来匹配特定级别的每个单词,然后遍历整个字典以查看其中的任何单词是否与此正则表达式匹配。构建实际可能的单词并检查每个单词是否在字典中肯定会更有效。也就是说,如果您有foo,请检查您的字典aooboocoo,... zoofao,{ {1}},fbo,... fcofzofoafob,... foc。由于您已经从字典中删除了访问过的字词,因此您无法在此列表中获得foz的多个新匹配。

这应该会显着减少您必须进行的检查次数,并且会从正则表达式测试更改为简单的属性查找。我猜测它会大大提升性能。