123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- // Copyright 2007 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /**
- * @fileoverview Datastructure: Trie.
- *
- *
- * This file provides the implementation of a trie data structure. A trie is a
- * data structure that stores key/value pairs in a prefix tree. See:
- * http://en.wikipedia.org/wiki/Trie
- */
- goog.provide('goog.structs.Trie');
- goog.require('goog.object');
- goog.require('goog.structs');
- /**
- * Class for a Trie datastructure. Trie data structures are made out of trees
- * of Trie classes.
- *
- * @param {goog.structs.Trie<VALUE>|Object<string, VALUE>=} opt_trie Optional
- * goog.structs.Trie or Object to initialize trie with.
- * @constructor
- * @template VALUE
- */
- goog.structs.Trie = function(opt_trie) {
- /**
- * This trie's value. For the base trie, this will be the value of the
- * empty key, if defined.
- * @private {VALUE}
- */
- this.value_ = undefined;
- /**
- * This trie's child nodes.
- * @private {!Object<!goog.structs.Trie<VALUE>>}
- */
- this.childNodes_ = {};
- if (opt_trie) {
- this.setAll(opt_trie);
- }
- };
- /**
- * Sets the given key/value pair in the trie. O(L), where L is the length
- * of the key.
- * @param {string} key The key.
- * @param {VALUE} value The value.
- */
- goog.structs.Trie.prototype.set = function(key, value) {
- this.setOrAdd_(key, value, false);
- };
- /**
- * Adds the given key/value pair in the trie. Throw an exception if the key
- * already exists in the trie. O(L), where L is the length of the key.
- * @param {string} key The key.
- * @param {VALUE} value The value.
- */
- goog.structs.Trie.prototype.add = function(key, value) {
- this.setOrAdd_(key, value, true);
- };
- /**
- * Helper function for set and add. Adds the given key/value pair to
- * the trie, or, if the key already exists, sets the value of the key. If
- * opt_add is true, then throws an exception if the key already has a value in
- * the trie. O(L), where L is the length of the key.
- * @param {string} key The key.
- * @param {VALUE} value The value.
- * @param {boolean=} opt_add Throw exception if key is already in the trie.
- * @private
- */
- goog.structs.Trie.prototype.setOrAdd_ = function(key, value, opt_add) {
- var node = this;
- for (var characterPosition = 0; characterPosition < key.length;
- characterPosition++) {
- var currentCharacter = key.charAt(characterPosition);
- if (!node.childNodes_[currentCharacter]) {
- node.childNodes_[currentCharacter] = new goog.structs.Trie();
- }
- node = node.childNodes_[currentCharacter];
- }
- if (opt_add && node.value_ !== undefined) {
- throw Error('The collection already contains the key "' + key + '"');
- } else {
- node.value_ = value;
- }
- };
- /**
- * Adds multiple key/value pairs from another goog.structs.Trie or Object.
- * O(N) where N is the number of nodes in the trie.
- * @param {!Object<string, VALUE>|!goog.structs.Trie<VALUE>} trie Object
- * containing the data to add.
- */
- goog.structs.Trie.prototype.setAll = function(trie) {
- var keys = goog.structs.getKeys(trie);
- var values = goog.structs.getValues(trie);
- for (var i = 0; i < keys.length; i++) {
- this.set(keys[i], values[i]);
- }
- };
- /**
- * Traverse along the given path, returns the child node at ending.
- * Returns undefined if node for the path doesn't exist.
- * @param {string} path The path to traverse.
- * @return {!goog.structs.Trie<VALUE>|undefined}
- * @private
- */
- goog.structs.Trie.prototype.getChildNode_ = function(path) {
- var node = this;
- for (var characterPosition = 0; characterPosition < path.length;
- characterPosition++) {
- var currentCharacter = path.charAt(characterPosition);
- node = node.childNodes_[currentCharacter];
- if (!node) {
- return undefined;
- }
- }
- return node;
- };
- /**
- * Retrieves a value from the trie given a key. O(L), where L is the length of
- * the key.
- * @param {string} key The key to retrieve from the trie.
- * @return {VALUE|undefined} The value of the key in the trie, or undefined if
- * the trie does not contain this key.
- */
- goog.structs.Trie.prototype.get = function(key) {
- var node = this.getChildNode_(key);
- return node ? node.value_ : undefined;
- };
- /**
- * Retrieves all values from the trie that correspond to prefixes of the given
- * input key. O(L), where L is the length of the key.
- *
- * @param {string} key The key to use for lookup. The given key as well as all
- * prefixes of the key are retrieved.
- * @param {?number=} opt_keyStartIndex Optional position in key to start lookup
- * from. Defaults to 0 if not specified.
- * @return {!Object<string, VALUE>} Map of end index of matching prefixes and
- * corresponding values. Empty if no match found.
- */
- goog.structs.Trie.prototype.getKeyAndPrefixes = function(
- key, opt_keyStartIndex) {
- var node = this;
- var matches = {};
- var characterPosition = opt_keyStartIndex || 0;
- if (node.value_ !== undefined) {
- matches[characterPosition] = node.value_;
- }
- for (; characterPosition < key.length; characterPosition++) {
- var currentCharacter = key.charAt(characterPosition);
- if (!(currentCharacter in node.childNodes_)) {
- break;
- }
- node = node.childNodes_[currentCharacter];
- if (node.value_ !== undefined) {
- matches[characterPosition] = node.value_;
- }
- }
- return matches;
- };
- /**
- * Gets the values of the trie. Not returned in any reliable order. O(N) where
- * N is the number of nodes in the trie. Calls getValuesInternal_.
- * @return {!Array<VALUE>} The values in the trie.
- */
- goog.structs.Trie.prototype.getValues = function() {
- var allValues = [];
- this.getValuesInternal_(allValues);
- return allValues;
- };
- /**
- * Gets the values of the trie. Not returned in any reliable order. O(N) where
- * N is the number of nodes in the trie. Builds the values as it goes.
- * @param {!Array<VALUE>} allValues Array to place values into.
- * @private
- */
- goog.structs.Trie.prototype.getValuesInternal_ = function(allValues) {
- if (this.value_ !== undefined) {
- allValues.push(this.value_);
- }
- for (var childNode in this.childNodes_) {
- this.childNodes_[childNode].getValuesInternal_(allValues);
- }
- };
- /**
- * Gets the keys of the trie. Not returned in any reliable order. O(N) where
- * N is the number of nodes in the trie (or prefix subtree).
- * @param {string=} opt_prefix Find only keys with this optional prefix.
- * @return {!Array<string>} The keys in the trie.
- */
- goog.structs.Trie.prototype.getKeys = function(opt_prefix) {
- var allKeys = [];
- if (opt_prefix) {
- // Traverse to the given prefix, then call getKeysInternal_ to dump the
- // keys below that point.
- var node = this;
- for (var characterPosition = 0; characterPosition < opt_prefix.length;
- characterPosition++) {
- var currentCharacter = opt_prefix.charAt(characterPosition);
- if (!node.childNodes_[currentCharacter]) {
- return [];
- }
- node = node.childNodes_[currentCharacter];
- }
- node.getKeysInternal_(opt_prefix, allKeys);
- } else {
- this.getKeysInternal_('', allKeys);
- }
- return allKeys;
- };
- /**
- * Private method to get keys from the trie. Builds the keys as it goes.
- * @param {string} keySoFar The partial key (prefix) traversed so far.
- * @param {!Array<string>} allKeys The partially built array of keys seen so
- * far.
- * @private
- */
- goog.structs.Trie.prototype.getKeysInternal_ = function(keySoFar, allKeys) {
- if (this.value_ !== undefined) {
- allKeys.push(keySoFar);
- }
- for (var childNode in this.childNodes_) {
- this.childNodes_[childNode].getKeysInternal_(keySoFar + childNode, allKeys);
- }
- };
- /**
- * Checks to see if a certain key is in the trie. O(L), where L is the length
- * of the key.
- * @param {string} key A key that may be in the trie.
- * @return {boolean} Whether the trie contains key.
- */
- goog.structs.Trie.prototype.containsKey = function(key) {
- return this.get(key) !== undefined;
- };
- /**
- * Checks to see if a certain prefix is in the trie. O(L), where L is the length
- * of the prefix.
- * @param {string} prefix A prefix that may be in the trie.
- * @return {boolean} Whether any key of the trie has the prefix.
- */
- goog.structs.Trie.prototype.containsPrefix = function(prefix) {
- // Empty string is any key's prefix.
- if (prefix.length == 0) {
- return !this.isEmpty();
- }
- return !!this.getChildNode_(prefix);
- };
- /**
- * Checks to see if a certain value is in the trie. Worst case is O(N) where
- * N is the number of nodes in the trie.
- * @param {VALUE} value A value that may be in the trie.
- * @return {boolean} Whether the trie contains the value.
- */
- goog.structs.Trie.prototype.containsValue = function(value) {
- if (this.value_ === value) {
- return true;
- }
- for (var childNode in this.childNodes_) {
- if (this.childNodes_[childNode].containsValue(value)) {
- return true;
- }
- }
- return false;
- };
- /**
- * Completely empties a trie of all keys and values. ~O(1)
- */
- goog.structs.Trie.prototype.clear = function() {
- this.childNodes_ = {};
- this.value_ = undefined;
- };
- /**
- * Removes a key from the trie or throws an exception if the key is not in the
- * trie. O(L), where L is the length of the key.
- * @param {string} key A key that should be removed from the trie.
- * @return {VALUE} The value whose key was removed.
- */
- goog.structs.Trie.prototype.remove = function(key) {
- var node = this;
- var parents = [];
- for (var characterPosition = 0; characterPosition < key.length;
- characterPosition++) {
- var currentCharacter = key.charAt(characterPosition);
- if (!node.childNodes_[currentCharacter]) {
- throw Error('The collection does not have the key "' + key + '"');
- }
- // Archive the current parent and child name (key in childNodes_) so that
- // we may remove the following node and its parents if they are empty.
- parents.push([node, currentCharacter]);
- node = node.childNodes_[currentCharacter];
- }
- var oldValue = node.value_;
- delete node.value_;
- while (parents.length > 0) {
- var currentParentAndCharacter = parents.pop();
- var currentParent = currentParentAndCharacter[0];
- var currentCharacter = currentParentAndCharacter[1];
- if (currentParent.childNodes_[currentCharacter].isEmpty()) {
- // If the child is empty, then remove it.
- delete currentParent.childNodes_[currentCharacter];
- } else {
- // No point of traversing back any further, since we can't remove this
- // path.
- break;
- }
- }
- return oldValue;
- };
- /**
- * Clones a trie and returns a new trie. O(N), where N is the number of nodes
- * in the trie.
- * @return {!goog.structs.Trie<VALUE>} A new goog.structs.Trie with the same
- * key value pairs.
- */
- goog.structs.Trie.prototype.clone = function() {
- return new goog.structs.Trie(this);
- };
- /**
- * Returns the number of key value pairs in the trie. O(N), where N is the
- * number of nodes in the trie.
- * TODO: This could be optimized by storing a weight (count below) in every
- * node.
- * @return {number} The number of pairs.
- */
- goog.structs.Trie.prototype.getCount = function() {
- return goog.structs.getCount(this.getValues());
- };
- /**
- * Returns true if this trie contains no elements. ~O(1).
- * @return {boolean} True iff this trie contains no elements.
- */
- goog.structs.Trie.prototype.isEmpty = function() {
- return this.value_ === undefined && goog.object.isEmpty(this.childNodes_);
- };
|