最近在作ASTR,而algorithm是该项目的task 1. 因此开始从新刷LeetCode题。相比其余ACM题库而言,LeetCode的难度属于初、中级,更加的偏向于职场,而弱化了一些高级数学相关的东西。javascript
这段话是我对于刷LeetCode的一些见解。不少人以为算法对于实际工做过程当中的用途不大,有点相似于“面试造火箭,工做拧螺丝”。可是真的用途不大吗?我以为并不是如此,若是你可以发现里面的数学之美的话。其一,一个优秀的算法能够极大的提升你的程序运行效率,尤为在某些极端状况下面;其二,学习了这些算法,能够极大的提升咱们的逻辑思惟;其三,这对于面试仍是有不少好处的。java
我作LeetCode的方法是:node
Note: 特别强调,刷题不是目的,会作某一到具体的题更不是完成结果。学会每道题背后的原理,并可以理解、完成全部的这一类问题,以及将其运用于本身的工做当中,这才是咱们真正须要达到的目标。git
举个例子:面试
本篇为trie树相关第一篇算法
首先Trie 来自于单词retrieval, 一般发音为 /ˈtraɪ/ (as "try").数组
In computer science, a trie, also called digital tree, radix tree or prefix tree, is a kind of search tree—an ordered tree data structure used to store a dynamic set or associative array where the keys are usually strings.数据结构
Trie tree,又被称为字典树或前缀树。从名字咱们能够推断出其能够被用来查找字符串。app
咱们先来看一个例子:学习
cat, cash, app, apple , aply, ok
,来构建一颗字典树, 以下图:最简单的两个操做为: insert 和 search
从图中能够直观看出来,从左到右扫描新单词,若是字母在相应根节点下没有出现过,就插入这个字母;不然沿着字典树往下走,看单词的下一个字母。
问题1: 字母往哪一个位置插? 有两种编码方式。第一种能够按照输入顺序对其进行编码,这里相同字母的编码可能不一样:
一般来说,咱们来实现这个数据结构会有两种方式:
一般,虽然第二种方式更加的浪费空间,可是我会更加的喜欢用第二种方式。好比在处理下面这几个问题时更加方便:
所以,咱们来看实际的代码(javascript版):
var TrieNode = function() {
this.isEnd = false;
this.links = new Array(26);
}
TrieNode.prototype.containsKey = function(ch) { // 当前节点的子节点中是否包含该字符
return this.links[ch.charCodeAt(0) - 'a'.charCodeAt(0)] !== undefined;
}
TrieNode.prototype.get = function(ch) { // 获取当前节点相关字符的子节点
return this.links[ch.charCodeAt(0) - 'a'.charCodeAt(0)];
}
TrieNode.prototype.put = function(ch, node) { // 插入当前相关字符的子节点
this.links[ch.charCodeAt(0) - 'a'.charCodeAt(0)] = node;
}
TrieNode.prototype.setEnd = function() { // 设置当前节点是否为单词结尾
this.isEnd =true
}
/** * Initialize your data structure here. */
var Trie = function() {
this.root = new TrieNode();
};
/** * Inserts a word into the trie. * @param {string} word * @return {void} */
Trie.prototype.insert = function(word) {
let node = this.root;
for (let i = 0; i < word.length; i++) {
let currentChar = word[i];
if(!node.containsKey(currentChar)) {
node.put(currentChar, new TrieNode());
}
node = node.get(currentChar);
}
node.setEnd();
};
/** * Returns if the word is in the trie. * @param {string} word * @return {boolean} */
Trie.prototype.search = function(word) {
let node = this.root;
for (let i = 0; i < word.length; i++) {
let currentChar = word[i];
if(node.containsKey(currentChar)) {
node = node.get(currentChar);
} else {
return false;
}
}
return node.isEnd();
};
/** * Returns if there is any word in the trie that starts with the given prefix. * @param {string} prefix * @return {boolean} */
Trie.prototype.startsWith = function(prefix) {
let node = this.root;
for (let i = 0; i < word.length; i++) {
let currentChar = word[i];
if(node.containsKey(currentChar)) {
node = node.get(currentChar);
} else {
return false;
}
}
return true;
};
/** */
var obj = new Trie();
let words = ["Trie","insert","search","search","startsWith","insert","search"]
for (let i = 0; i < words.length; i++ ) {
obj.insert(words[i]);
}
复制代码
很明显,上面的写法比较偏向于工程化,比较完整类型的,上面的代码能够更加的优化,咱们用对象来模拟:
var Trie = function() {
this.root = {};
};
/** * Inserts a word into the trie. * @param {string} word * @return {void} */
Trie.prototype.insert = function(word) {
let node = this.root;
for (let ch of word) {
if (!(ch in node)) node[ch] = {}
node = node[ch]
}
node['$'] = true // 表示单词结束位置
};
/** * Returns if the word is in the trie. * @param {string} word * @return {boolean} */
Trie.prototype.search = function(word) {
let node = this.root;
for(let ch of word) {
if(ch in node) node = node[ch]
else return false
}
return node['$'] === true;
};
/** * Returns if there is any word in the trie that starts with the given prefix. * @param {string} prefix * @return {boolean} */
Trie.prototype.startsWith = function(prefix) {
let node = this.root;
for(let ch of prefix) {
if(ch in node) node = node[ch]
else return false
}
return true;
};
复制代码
咱们来看看两段代码的运行效率:
咱们下面来看看实际开发中trie tree的运用:
autocomplete
spell checker
IP routing(longest prefix matching)