Add Trie.deleteWord and TrieNode.removeChild (#181)

This commit is contained in:
Kevin Brewer 2018-08-27 07:33:16 -05:00 committed by Oleksii Trekhleb
parent 6e2ff9b604
commit d25eff49e6
4 changed files with 96 additions and 0 deletions

View File

@ -24,6 +24,35 @@ export default class Trie {
return this;
}
/**
* @param {string} word
* @return {Trie}
*/
deleteWord(word) {
function depthFirstDelete(currentNode, charIndex) {
if (charIndex >= word.length) return;
const character = word[charIndex];
const nextNode = currentNode.getChild(character);
if (nextNode == null) return;
depthFirstDelete(nextNode, charIndex + 1);
if (charIndex === word.length - 1) {
nextNode.isCompleteWord = false;
}
// childNode is deleted only if:
// - childNode has NO children
// - childNode.isCompleteWord === false
currentNode.removeChild(character);
}
depthFirstDelete(this.head, 0);
return this;
}
/**
* @param {string} word
* @return {string[]}

View File

@ -37,6 +37,31 @@ export default class TrieNode {
return childNode;
}
/**
* @param {string} character
* @return {TrieNode}
*/
removeChild(character) {
function isSafeToDelete(node) {
return (
node
&& !node.isCompleteWord
&& node.children.getKeys().length === 0
);
}
const childNode = this.getChild(character);
// delete childNode only if:
// - childNode has NO children
// - childNode.isCompleteWord === false
if (isSafeToDelete(childNode)) {
this.children.delete(character);
}
return this;
}
/**
* @param {string} character
* @return {boolean}

View File

@ -23,6 +23,18 @@ describe('Trie', () => {
expect(trie.head.getChild('c').getChild('a').getChild('t').toString()).toBe('t*');
});
it('should delete words from trie', () => {
const trie = new Trie();
trie.addWord('carpet');
trie.addWord('car');
expect(trie.doesWordExist('carpet')).toBe(true);
trie.deleteWord('carpet');
expect(trie.doesWordExist('carpet')).toEqual(false);
expect(trie.doesWordExist('car')).toEqual(true);
});
it('should suggests next characters', () => {
const trie = new Trie();

View File

@ -18,6 +18,36 @@ describe('TrieNode', () => {
expect(trieNode.toString()).toBe('c:a,o');
});
describe('removing child nodes', () => {
it('should delete child node if the child node has NO children', () => {
const trieNode = new TrieNode('c');
trieNode.addChild('a');
expect(trieNode.hasChild('a')).toBe(true);
trieNode.removeChild('a');
expect(trieNode.hasChild('a')).toBe(false);
});
it('should NOT delete child node if the child node has children', () => {
const trieNode = new TrieNode('c');
trieNode.addChild('a');
const childNode = trieNode.getChild('a');
childNode.addChild('r');
trieNode.removeChild('a');
expect(trieNode.hasChild('a')).toEqual(true);
});
it('should NOT delete child node if the child node completes a word', () => {
const trieNode = new TrieNode('c');
const IS_COMPLETE_WORD = true;
trieNode.addChild('a', IS_COMPLETE_WORD);
trieNode.removeChild('a');
expect(trieNode.hasChild('a')).toEqual(true);
});
});
it('should get child nodes', () => {
const trieNode = new TrieNode('c');