stakach-algorithms 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +97 -97
- data/Rakefile +27 -27
- data/ext/algorithms/string/extconf.rb +4 -4
- data/ext/algorithms/string/string.c +70 -70
- data/ext/containers/bst/bst.c +249 -249
- data/ext/containers/bst/extconf.rb +4 -4
- data/ext/containers/deque/deque.c +248 -248
- data/ext/containers/deque/extconf.rb +4 -4
- data/ext/containers/rbtree_map/extconf.rb +4 -4
- data/ext/containers/rbtree_map/rbtree.c +500 -500
- data/ext/containers/splaytree_map/extconf.rb +4 -4
- data/ext/containers/splaytree_map/splaytree.c +421 -421
- data/lib/algorithms/search.rb +85 -85
- data/lib/algorithms/sort.rb +242 -242
- data/lib/algorithms/string.rb +10 -10
- data/lib/algorithms/version.rb +1 -1
- data/lib/algorithms.rb +69 -69
- data/lib/containers/deque.rb +176 -176
- data/lib/containers/heap.rb +506 -506
- data/lib/containers/kd_tree.rb +112 -112
- data/lib/containers/priority_queue.rb +116 -116
- data/lib/containers/queue.rb +71 -71
- data/lib/containers/rb_tree_map.rb +402 -402
- data/lib/containers/splay_tree_map.rb +273 -273
- data/lib/containers/stack.rb +70 -70
- data/lib/containers/suffix_array.rb +71 -71
- data/lib/containers/trie.rb +187 -187
- metadata +3 -3
data/lib/containers/stack.rb
CHANGED
@@ -1,71 +1,71 @@
|
|
1
|
-
require 'containers/deque'
|
2
|
-
=begin rdoc
|
3
|
-
A Stack is a container that keeps elements in a last-in first-out (LIFO) order. There are many
|
4
|
-
uses for stacks, including prefix-infix-postfix conversion and backtracking problems.
|
5
|
-
|
6
|
-
This implementation uses a doubly-linked list, guaranteeing O(1) complexity for all operations.
|
7
|
-
|
8
|
-
=end
|
9
|
-
module Algorithms
|
10
|
-
module Containers
|
11
|
-
class Stack
|
12
|
-
include Enumerable
|
13
|
-
# Create a new stack. Takes an optional array argument to initialize the stack.
|
14
|
-
#
|
15
|
-
# s = Algorithms::Containers::Stack.new([1, 2, 3])
|
16
|
-
# s.pop #=> 3
|
17
|
-
# s.pop #=> 2
|
18
|
-
def initialize(ary=[])
|
19
|
-
@container = Deque.new(ary)
|
20
|
-
end
|
21
|
-
|
22
|
-
# Returns the next item from the stack but does not remove it.
|
23
|
-
#
|
24
|
-
# s = Algorithms::Containers::Stack.new([1, 2, 3])
|
25
|
-
# s.next #=> 3
|
26
|
-
# s.size #=> 3
|
27
|
-
def next
|
28
|
-
@container.back
|
29
|
-
end
|
30
|
-
|
31
|
-
# Adds an item to the stack.
|
32
|
-
#
|
33
|
-
# s = Algorithms::Containers::Stack.new([1])
|
34
|
-
# s.push(2)
|
35
|
-
# s.pop #=> 2
|
36
|
-
# s.pop #=> 1
|
37
|
-
def push(obj)
|
38
|
-
@container.push_back(obj)
|
39
|
-
end
|
40
|
-
alias_method :<<, :push
|
41
|
-
|
42
|
-
# Removes the next item from the stack and returns it.
|
43
|
-
#
|
44
|
-
# s = Algorithms::Containers::Stack.new([1, 2, 3])
|
45
|
-
# s.pop #=> 3
|
46
|
-
# s.size #=> 2
|
47
|
-
def pop
|
48
|
-
@container.pop_back
|
49
|
-
end
|
50
|
-
|
51
|
-
# Return the number of items in the stack.
|
52
|
-
#
|
53
|
-
# s = Algorithms::Containers::Stack.new([1, 2, 3])
|
54
|
-
# s.size #=> 3
|
55
|
-
def size
|
56
|
-
@container.size
|
57
|
-
end
|
58
|
-
|
59
|
-
# Returns true if the stack is empty, false otherwise.
|
60
|
-
def empty?
|
61
|
-
@container.empty?
|
62
|
-
end
|
63
|
-
|
64
|
-
# Iterate over the Stack in LIFO order.
|
65
|
-
def each(&block)
|
66
|
-
@container.each_backward(&block)
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|
70
|
-
end
|
1
|
+
require 'containers/deque'
|
2
|
+
=begin rdoc
|
3
|
+
A Stack is a container that keeps elements in a last-in first-out (LIFO) order. There are many
|
4
|
+
uses for stacks, including prefix-infix-postfix conversion and backtracking problems.
|
5
|
+
|
6
|
+
This implementation uses a doubly-linked list, guaranteeing O(1) complexity for all operations.
|
7
|
+
|
8
|
+
=end
|
9
|
+
module Algorithms
|
10
|
+
module Containers
|
11
|
+
class Stack
|
12
|
+
include Enumerable
|
13
|
+
# Create a new stack. Takes an optional array argument to initialize the stack.
|
14
|
+
#
|
15
|
+
# s = Algorithms::Containers::Stack.new([1, 2, 3])
|
16
|
+
# s.pop #=> 3
|
17
|
+
# s.pop #=> 2
|
18
|
+
def initialize(ary=[])
|
19
|
+
@container = Deque.new(ary)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the next item from the stack but does not remove it.
|
23
|
+
#
|
24
|
+
# s = Algorithms::Containers::Stack.new([1, 2, 3])
|
25
|
+
# s.next #=> 3
|
26
|
+
# s.size #=> 3
|
27
|
+
def next
|
28
|
+
@container.back
|
29
|
+
end
|
30
|
+
|
31
|
+
# Adds an item to the stack.
|
32
|
+
#
|
33
|
+
# s = Algorithms::Containers::Stack.new([1])
|
34
|
+
# s.push(2)
|
35
|
+
# s.pop #=> 2
|
36
|
+
# s.pop #=> 1
|
37
|
+
def push(obj)
|
38
|
+
@container.push_back(obj)
|
39
|
+
end
|
40
|
+
alias_method :<<, :push
|
41
|
+
|
42
|
+
# Removes the next item from the stack and returns it.
|
43
|
+
#
|
44
|
+
# s = Algorithms::Containers::Stack.new([1, 2, 3])
|
45
|
+
# s.pop #=> 3
|
46
|
+
# s.size #=> 2
|
47
|
+
def pop
|
48
|
+
@container.pop_back
|
49
|
+
end
|
50
|
+
|
51
|
+
# Return the number of items in the stack.
|
52
|
+
#
|
53
|
+
# s = Algorithms::Containers::Stack.new([1, 2, 3])
|
54
|
+
# s.size #=> 3
|
55
|
+
def size
|
56
|
+
@container.size
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns true if the stack is empty, false otherwise.
|
60
|
+
def empty?
|
61
|
+
@container.empty?
|
62
|
+
end
|
63
|
+
|
64
|
+
# Iterate over the Stack in LIFO order.
|
65
|
+
def each(&block)
|
66
|
+
@container.each_backward(&block)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
71
|
end
|
@@ -1,72 +1,72 @@
|
|
1
|
-
=begin rdoc
|
2
|
-
A suffix array enables fast substring search of a given string. An array of all possible substrings
|
3
|
-
is constructed and stored, and a binary search is then done to find a desired substring among those
|
4
|
-
stored. While more storage (and thus memory) is needed to create the SuffixArray, the advantage is
|
5
|
-
that substrings can be found in O(m log n) time, where m is the length of the substring to search for
|
6
|
-
and n is the total number of substrings.
|
7
|
-
|
8
|
-
=end
|
9
|
-
module Algorithms
|
10
|
-
module Containers
|
11
|
-
class SuffixArray
|
12
|
-
# Creates a new SuffixArray with a given string. Object of any class implementing a #to_s method can
|
13
|
-
# be passed in, such as integers.
|
14
|
-
#
|
15
|
-
# Complexity: O(n^2 log n)
|
16
|
-
#
|
17
|
-
# s_array = Algorithms::Containers::SuffixArray.new("abracadabra")
|
18
|
-
# s_array["abra"] #=> true
|
19
|
-
#
|
20
|
-
# number = Algorithms::Containers::SuffixArray.new(1234567)
|
21
|
-
# number[1] #=> true
|
22
|
-
# number[13] #=> false
|
23
|
-
def initialize(string)
|
24
|
-
string = string.to_s
|
25
|
-
raise ArgumentError, "SuffixArray needs to be initialized with a non-empty string" if string.empty?
|
26
|
-
@original_string = string
|
27
|
-
@suffixes = []
|
28
|
-
string.length.times do |i|
|
29
|
-
@suffixes << string[i..-1]
|
30
|
-
end
|
31
|
-
|
32
|
-
# Sort the suffixes in ascending order
|
33
|
-
@suffixes.sort! { |x, y| x <=> y }
|
34
|
-
end
|
35
|
-
|
36
|
-
# Returns true if the substring occurs in the string, false otherwise.
|
37
|
-
#
|
38
|
-
# Complexity: O(m + log n)
|
39
|
-
#
|
40
|
-
# s_array = Algorithms::Containers::SuffixArray.new("abracadabra")
|
41
|
-
# s_array.has_substring?("a") #=> true
|
42
|
-
# s_array.has_substring?("abra") #=> true
|
43
|
-
# s_array.has_substring?("abracadabra") #=> true
|
44
|
-
# s_array.has_substring?("acadabra") #=> true
|
45
|
-
# s_array.has_substring?("adabra") #=> true
|
46
|
-
# s_array.has_substring?("bra") #=> true
|
47
|
-
# s_array.has_substring?("bracadabra") #=> true
|
48
|
-
# s_array.has_substring?("cadabra") #=> true
|
49
|
-
# s_array.has_substring?("dabra") #=> true
|
50
|
-
# s_array.has_substring?("ra") #=> true
|
51
|
-
# s_array.has_substring?("racadabra") #=> true
|
52
|
-
# s_array.has_substring?("nope") #=> false
|
53
|
-
def has_substring?(substring)
|
54
|
-
substring = substring.to_s
|
55
|
-
return false if substring.empty?
|
56
|
-
substring_length = substring.length-1
|
57
|
-
l, r = 0, @suffixes.size-1
|
58
|
-
while(l <= r)
|
59
|
-
mid = (l + r) / 2
|
60
|
-
suffix = @suffixes[mid][0..substring_length]
|
61
|
-
case substring <=> suffix
|
62
|
-
when 0 then return true
|
63
|
-
when 1 then l = mid + 1
|
64
|
-
when -1 then r = mid - 1
|
65
|
-
end
|
66
|
-
end
|
67
|
-
return false
|
68
|
-
end
|
69
|
-
alias_method :[], :has_substring?
|
70
|
-
end
|
71
|
-
end
|
1
|
+
=begin rdoc
|
2
|
+
A suffix array enables fast substring search of a given string. An array of all possible substrings
|
3
|
+
is constructed and stored, and a binary search is then done to find a desired substring among those
|
4
|
+
stored. While more storage (and thus memory) is needed to create the SuffixArray, the advantage is
|
5
|
+
that substrings can be found in O(m log n) time, where m is the length of the substring to search for
|
6
|
+
and n is the total number of substrings.
|
7
|
+
|
8
|
+
=end
|
9
|
+
module Algorithms
|
10
|
+
module Containers
|
11
|
+
class SuffixArray
|
12
|
+
# Creates a new SuffixArray with a given string. Object of any class implementing a #to_s method can
|
13
|
+
# be passed in, such as integers.
|
14
|
+
#
|
15
|
+
# Complexity: O(n^2 log n)
|
16
|
+
#
|
17
|
+
# s_array = Algorithms::Containers::SuffixArray.new("abracadabra")
|
18
|
+
# s_array["abra"] #=> true
|
19
|
+
#
|
20
|
+
# number = Algorithms::Containers::SuffixArray.new(1234567)
|
21
|
+
# number[1] #=> true
|
22
|
+
# number[13] #=> false
|
23
|
+
def initialize(string)
|
24
|
+
string = string.to_s
|
25
|
+
raise ArgumentError, "SuffixArray needs to be initialized with a non-empty string" if string.empty?
|
26
|
+
@original_string = string
|
27
|
+
@suffixes = []
|
28
|
+
string.length.times do |i|
|
29
|
+
@suffixes << string[i..-1]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Sort the suffixes in ascending order
|
33
|
+
@suffixes.sort! { |x, y| x <=> y }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns true if the substring occurs in the string, false otherwise.
|
37
|
+
#
|
38
|
+
# Complexity: O(m + log n)
|
39
|
+
#
|
40
|
+
# s_array = Algorithms::Containers::SuffixArray.new("abracadabra")
|
41
|
+
# s_array.has_substring?("a") #=> true
|
42
|
+
# s_array.has_substring?("abra") #=> true
|
43
|
+
# s_array.has_substring?("abracadabra") #=> true
|
44
|
+
# s_array.has_substring?("acadabra") #=> true
|
45
|
+
# s_array.has_substring?("adabra") #=> true
|
46
|
+
# s_array.has_substring?("bra") #=> true
|
47
|
+
# s_array.has_substring?("bracadabra") #=> true
|
48
|
+
# s_array.has_substring?("cadabra") #=> true
|
49
|
+
# s_array.has_substring?("dabra") #=> true
|
50
|
+
# s_array.has_substring?("ra") #=> true
|
51
|
+
# s_array.has_substring?("racadabra") #=> true
|
52
|
+
# s_array.has_substring?("nope") #=> false
|
53
|
+
def has_substring?(substring)
|
54
|
+
substring = substring.to_s
|
55
|
+
return false if substring.empty?
|
56
|
+
substring_length = substring.length-1
|
57
|
+
l, r = 0, @suffixes.size-1
|
58
|
+
while(l <= r)
|
59
|
+
mid = (l + r) / 2
|
60
|
+
suffix = @suffixes[mid][0..substring_length]
|
61
|
+
case substring <=> suffix
|
62
|
+
when 0 then return true
|
63
|
+
when 1 then l = mid + 1
|
64
|
+
when -1 then r = mid - 1
|
65
|
+
end
|
66
|
+
end
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
alias_method :[], :has_substring?
|
70
|
+
end
|
71
|
+
end
|
72
72
|
end
|
data/lib/containers/trie.rb
CHANGED
@@ -1,188 +1,188 @@
|
|
1
|
-
=begin rdoc
|
2
|
-
A Trie is a data structure that stores key value pairs in a tree-like fashion. It allows
|
3
|
-
O(m) lookup speed, where m is the length of the key searched, and has no chance of collisions,
|
4
|
-
unlike hash tables. Because of its nature, search misses are quickly detected.
|
5
|
-
|
6
|
-
Tries are often used for longest prefix algorithms, wildcard matching, and can be used to
|
7
|
-
implement a radix sort.
|
8
|
-
|
9
|
-
This implemention is based on a Ternary Search Tree.
|
10
|
-
=end
|
11
|
-
module Algorithms
|
12
|
-
module Containers
|
13
|
-
class Trie
|
14
|
-
# Create a new, empty Trie.
|
15
|
-
#
|
16
|
-
# t =
|
17
|
-
# Trie.new
|
18
|
-
# t["hello"] = "world"
|
19
|
-
# t["hello] #=> "world"
|
20
|
-
def initialize
|
21
|
-
@root = nil
|
22
|
-
end
|
23
|
-
|
24
|
-
# Adds a key, value pair to the Trie, and returns the value if successful. The to_s method is
|
25
|
-
# called on the parameter to turn it into a string.
|
26
|
-
#
|
27
|
-
# Complexity: O(m)
|
28
|
-
#
|
29
|
-
# t = Algorithms::Containers::Trie.new
|
30
|
-
# t["hello"] = "world"
|
31
|
-
# t.push("hello", "world") # does the same thing
|
32
|
-
# t["hello"] #=> "world"
|
33
|
-
# t[1] = 1
|
34
|
-
# t[1] #=> 1
|
35
|
-
def push(key, value)
|
36
|
-
key = key.to_s
|
37
|
-
return nil if key.empty?
|
38
|
-
@root = push_recursive(@root, key, 0, value)
|
39
|
-
value
|
40
|
-
end
|
41
|
-
alias_method :[]=, :push
|
42
|
-
|
43
|
-
# Returns true if the key is contained in the Trie.
|
44
|
-
#
|
45
|
-
# Complexity: O(m) worst case
|
46
|
-
#
|
47
|
-
def has_key?(key)
|
48
|
-
key = key.to_s
|
49
|
-
return false if key.empty?
|
50
|
-
!(get_recursive(@root, key, 0).nil?)
|
51
|
-
end
|
52
|
-
alias_method :include?, :has_key?
|
53
|
-
|
54
|
-
# Returns the value of the desired key, or nil if the key doesn't exist.
|
55
|
-
#
|
56
|
-
# Complexity: O(m) worst case
|
57
|
-
#
|
58
|
-
# t = Algorithms::Containers::Trie.new
|
59
|
-
# t.get("hello") = "world"
|
60
|
-
# t.get("non-existant") #=> nil
|
61
|
-
def get(key)
|
62
|
-
key = key.to_s
|
63
|
-
return nil if key.empty?
|
64
|
-
node = get_recursive(@root, key, 0)
|
65
|
-
node ? node.last : nil
|
66
|
-
end
|
67
|
-
alias_method :[], :get
|
68
|
-
|
69
|
-
# Returns the longest key that has a prefix in common with the parameter string. If
|
70
|
-
# no match is found, the blank string "" is returned.
|
71
|
-
#
|
72
|
-
# Complexity: O(m) worst case
|
73
|
-
#
|
74
|
-
# t = Algorithms::Containers::Trie.new
|
75
|
-
# t.push("Hello", "World")
|
76
|
-
# t.push("Hello, brother", "World")
|
77
|
-
# t.push("Hello, bob", "World")
|
78
|
-
# t.longest_prefix("Hello, brandon") #=> "Hello"
|
79
|
-
# t.longest_prefix("Hel") #=> ""
|
80
|
-
# t.longest_prefix("Hello") #=> "Hello"
|
81
|
-
def longest_prefix(string)
|
82
|
-
string = string.to_s
|
83
|
-
return nil if string.empty?
|
84
|
-
len = prefix_recursive(@root, string, 0)
|
85
|
-
string[0...len]
|
86
|
-
end
|
87
|
-
|
88
|
-
# Returns a sorted array containing strings that match the parameter string. The wildcard
|
89
|
-
# characters that match any character are '*' and '.' If no match is found, an empty
|
90
|
-
# array is returned.
|
91
|
-
#
|
92
|
-
# Complexity: O(n) worst case
|
93
|
-
#
|
94
|
-
# t = Algorithms::Containers::Trie.new
|
95
|
-
# t.push("Hello", "World")
|
96
|
-
# t.push("Hilly", "World")
|
97
|
-
# t.push("Hello, bob", "World")
|
98
|
-
# t.wildcard("H*ll.") #=> ["Hello", "Hilly"]
|
99
|
-
# t.wildcard("Hel") #=> []
|
100
|
-
def wildcard(string)
|
101
|
-
string = string.to_s
|
102
|
-
return nil if string.empty?
|
103
|
-
ary = []
|
104
|
-
ary << wildcard_recursive(@root, string, 0, "")
|
105
|
-
ary.flatten.compact.sort
|
106
|
-
end
|
107
|
-
|
108
|
-
class Node # :nodoc: all
|
109
|
-
attr_accessor :left, :mid, :right, :char, :value, :end
|
110
|
-
|
111
|
-
def initialize(char, value)
|
112
|
-
@char = char
|
113
|
-
@value = value
|
114
|
-
@left = @mid = @right = nil
|
115
|
-
@end = false
|
116
|
-
end
|
117
|
-
|
118
|
-
def last?
|
119
|
-
@end == true
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def wildcard_recursive(node, string, index, prefix)
|
124
|
-
return nil if node.nil? || index == string.length
|
125
|
-
arr = []
|
126
|
-
char = string[index]
|
127
|
-
if (char.chr == "*" || char.chr == "." || char < node.char)
|
128
|
-
arr << wildcard_recursive(node.left, string, index, prefix)
|
129
|
-
end
|
130
|
-
if (char.chr == "*" || char.chr == "." || char > node.char)
|
131
|
-
arr << wildcard_recursive(node.right, string, index, prefix)
|
132
|
-
end
|
133
|
-
if (char.chr == "*" || char.chr == "." || char == node.char)
|
134
|
-
arr << "#{prefix}#{node.char.chr}" if node.last?
|
135
|
-
arr << wildcard_recursive(node.mid, string, index+1, prefix + node.char.chr)
|
136
|
-
end
|
137
|
-
arr
|
138
|
-
end
|
139
|
-
|
140
|
-
def prefix_recursive(node, string, index)
|
141
|
-
return 0 if node.nil? || index == string.length
|
142
|
-
len = 0
|
143
|
-
rec_len = 0
|
144
|
-
char = string[index]
|
145
|
-
if (char < node.char)
|
146
|
-
rec_len = prefix_recursive(node.left, string, index)
|
147
|
-
elsif (char > node.char)
|
148
|
-
rec_len = prefix_recursive(node.right, string, index)
|
149
|
-
else
|
150
|
-
len = index+1 if node.last?
|
151
|
-
rec_len = prefix_recursive(node.mid, string, index+1)
|
152
|
-
end
|
153
|
-
len > rec_len ? len : rec_len
|
154
|
-
end
|
155
|
-
|
156
|
-
def push_recursive(node, string, index, value)
|
157
|
-
char = string[index]
|
158
|
-
node = Node.new(char, value) if node.nil?
|
159
|
-
if (char < node.char)
|
160
|
-
node.left = push_recursive(node.left, string, index, value)
|
161
|
-
elsif (char > node.char)
|
162
|
-
node.right = push_recursive(node.right, string, index, value)
|
163
|
-
elsif (index < string.length-1) # We're not at the end of the input string; add next char
|
164
|
-
node.mid = push_recursive(node.mid, string, index+1, value)
|
165
|
-
else
|
166
|
-
node.end = true
|
167
|
-
node.value = value
|
168
|
-
end
|
169
|
-
node
|
170
|
-
end
|
171
|
-
|
172
|
-
# Returns [char, value] if found
|
173
|
-
def get_recursive(node, string, index)
|
174
|
-
return nil if node.nil?
|
175
|
-
char = string[index]
|
176
|
-
if (char < node.char)
|
177
|
-
return get_recursive(node.left, string, index)
|
178
|
-
elsif (char > node.char)
|
179
|
-
return get_recursive(node.right, string, index)
|
180
|
-
elsif (index < string.length-1) # We're not at the end of the input string; add next char
|
181
|
-
return get_recursive(node.mid, string, index+1)
|
182
|
-
else
|
183
|
-
return node.last? ? [node.char, node.value] : nil
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
1
|
+
=begin rdoc
|
2
|
+
A Trie is a data structure that stores key value pairs in a tree-like fashion. It allows
|
3
|
+
O(m) lookup speed, where m is the length of the key searched, and has no chance of collisions,
|
4
|
+
unlike hash tables. Because of its nature, search misses are quickly detected.
|
5
|
+
|
6
|
+
Tries are often used for longest prefix algorithms, wildcard matching, and can be used to
|
7
|
+
implement a radix sort.
|
8
|
+
|
9
|
+
This implemention is based on a Ternary Search Tree.
|
10
|
+
=end
|
11
|
+
module Algorithms
|
12
|
+
module Containers
|
13
|
+
class Trie
|
14
|
+
# Create a new, empty Trie.
|
15
|
+
#
|
16
|
+
# t =
|
17
|
+
# Trie.new
|
18
|
+
# t["hello"] = "world"
|
19
|
+
# t["hello] #=> "world"
|
20
|
+
def initialize
|
21
|
+
@root = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# Adds a key, value pair to the Trie, and returns the value if successful. The to_s method is
|
25
|
+
# called on the parameter to turn it into a string.
|
26
|
+
#
|
27
|
+
# Complexity: O(m)
|
28
|
+
#
|
29
|
+
# t = Algorithms::Containers::Trie.new
|
30
|
+
# t["hello"] = "world"
|
31
|
+
# t.push("hello", "world") # does the same thing
|
32
|
+
# t["hello"] #=> "world"
|
33
|
+
# t[1] = 1
|
34
|
+
# t[1] #=> 1
|
35
|
+
def push(key, value)
|
36
|
+
key = key.to_s
|
37
|
+
return nil if key.empty?
|
38
|
+
@root = push_recursive(@root, key, 0, value)
|
39
|
+
value
|
40
|
+
end
|
41
|
+
alias_method :[]=, :push
|
42
|
+
|
43
|
+
# Returns true if the key is contained in the Trie.
|
44
|
+
#
|
45
|
+
# Complexity: O(m) worst case
|
46
|
+
#
|
47
|
+
def has_key?(key)
|
48
|
+
key = key.to_s
|
49
|
+
return false if key.empty?
|
50
|
+
!(get_recursive(@root, key, 0).nil?)
|
51
|
+
end
|
52
|
+
alias_method :include?, :has_key?
|
53
|
+
|
54
|
+
# Returns the value of the desired key, or nil if the key doesn't exist.
|
55
|
+
#
|
56
|
+
# Complexity: O(m) worst case
|
57
|
+
#
|
58
|
+
# t = Algorithms::Containers::Trie.new
|
59
|
+
# t.get("hello") = "world"
|
60
|
+
# t.get("non-existant") #=> nil
|
61
|
+
def get(key)
|
62
|
+
key = key.to_s
|
63
|
+
return nil if key.empty?
|
64
|
+
node = get_recursive(@root, key, 0)
|
65
|
+
node ? node.last : nil
|
66
|
+
end
|
67
|
+
alias_method :[], :get
|
68
|
+
|
69
|
+
# Returns the longest key that has a prefix in common with the parameter string. If
|
70
|
+
# no match is found, the blank string "" is returned.
|
71
|
+
#
|
72
|
+
# Complexity: O(m) worst case
|
73
|
+
#
|
74
|
+
# t = Algorithms::Containers::Trie.new
|
75
|
+
# t.push("Hello", "World")
|
76
|
+
# t.push("Hello, brother", "World")
|
77
|
+
# t.push("Hello, bob", "World")
|
78
|
+
# t.longest_prefix("Hello, brandon") #=> "Hello"
|
79
|
+
# t.longest_prefix("Hel") #=> ""
|
80
|
+
# t.longest_prefix("Hello") #=> "Hello"
|
81
|
+
def longest_prefix(string)
|
82
|
+
string = string.to_s
|
83
|
+
return nil if string.empty?
|
84
|
+
len = prefix_recursive(@root, string, 0)
|
85
|
+
string[0...len]
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns a sorted array containing strings that match the parameter string. The wildcard
|
89
|
+
# characters that match any character are '*' and '.' If no match is found, an empty
|
90
|
+
# array is returned.
|
91
|
+
#
|
92
|
+
# Complexity: O(n) worst case
|
93
|
+
#
|
94
|
+
# t = Algorithms::Containers::Trie.new
|
95
|
+
# t.push("Hello", "World")
|
96
|
+
# t.push("Hilly", "World")
|
97
|
+
# t.push("Hello, bob", "World")
|
98
|
+
# t.wildcard("H*ll.") #=> ["Hello", "Hilly"]
|
99
|
+
# t.wildcard("Hel") #=> []
|
100
|
+
def wildcard(string)
|
101
|
+
string = string.to_s
|
102
|
+
return nil if string.empty?
|
103
|
+
ary = []
|
104
|
+
ary << wildcard_recursive(@root, string, 0, "")
|
105
|
+
ary.flatten.compact.sort
|
106
|
+
end
|
107
|
+
|
108
|
+
class Node # :nodoc: all
|
109
|
+
attr_accessor :left, :mid, :right, :char, :value, :end
|
110
|
+
|
111
|
+
def initialize(char, value)
|
112
|
+
@char = char
|
113
|
+
@value = value
|
114
|
+
@left = @mid = @right = nil
|
115
|
+
@end = false
|
116
|
+
end
|
117
|
+
|
118
|
+
def last?
|
119
|
+
@end == true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def wildcard_recursive(node, string, index, prefix)
|
124
|
+
return nil if node.nil? || index == string.length
|
125
|
+
arr = []
|
126
|
+
char = string[index]
|
127
|
+
if (char.chr == "*" || char.chr == "." || char < node.char)
|
128
|
+
arr << wildcard_recursive(node.left, string, index, prefix)
|
129
|
+
end
|
130
|
+
if (char.chr == "*" || char.chr == "." || char > node.char)
|
131
|
+
arr << wildcard_recursive(node.right, string, index, prefix)
|
132
|
+
end
|
133
|
+
if (char.chr == "*" || char.chr == "." || char == node.char)
|
134
|
+
arr << "#{prefix}#{node.char.chr}" if node.last?
|
135
|
+
arr << wildcard_recursive(node.mid, string, index+1, prefix + node.char.chr)
|
136
|
+
end
|
137
|
+
arr
|
138
|
+
end
|
139
|
+
|
140
|
+
def prefix_recursive(node, string, index)
|
141
|
+
return 0 if node.nil? || index == string.length
|
142
|
+
len = 0
|
143
|
+
rec_len = 0
|
144
|
+
char = string[index]
|
145
|
+
if (char < node.char)
|
146
|
+
rec_len = prefix_recursive(node.left, string, index)
|
147
|
+
elsif (char > node.char)
|
148
|
+
rec_len = prefix_recursive(node.right, string, index)
|
149
|
+
else
|
150
|
+
len = index+1 if node.last?
|
151
|
+
rec_len = prefix_recursive(node.mid, string, index+1)
|
152
|
+
end
|
153
|
+
len > rec_len ? len : rec_len
|
154
|
+
end
|
155
|
+
|
156
|
+
def push_recursive(node, string, index, value)
|
157
|
+
char = string[index]
|
158
|
+
node = Node.new(char, value) if node.nil?
|
159
|
+
if (char < node.char)
|
160
|
+
node.left = push_recursive(node.left, string, index, value)
|
161
|
+
elsif (char > node.char)
|
162
|
+
node.right = push_recursive(node.right, string, index, value)
|
163
|
+
elsif (index < string.length-1) # We're not at the end of the input string; add next char
|
164
|
+
node.mid = push_recursive(node.mid, string, index+1, value)
|
165
|
+
else
|
166
|
+
node.end = true
|
167
|
+
node.value = value
|
168
|
+
end
|
169
|
+
node
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns [char, value] if found
|
173
|
+
def get_recursive(node, string, index)
|
174
|
+
return nil if node.nil?
|
175
|
+
char = string[index]
|
176
|
+
if (char < node.char)
|
177
|
+
return get_recursive(node.left, string, index)
|
178
|
+
elsif (char > node.char)
|
179
|
+
return get_recursive(node.right, string, index)
|
180
|
+
elsif (index < string.length-1) # We're not at the end of the input string; add next char
|
181
|
+
return get_recursive(node.mid, string, index+1)
|
182
|
+
else
|
183
|
+
return node.last? ? [node.char, node.value] : nil
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
188
|
end
|