dsa-ruby 1.0.0 → 1.0.2

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.
@@ -1,34 +1,80 @@
1
- module DSA
2
- class Stack
3
- def initialize
4
- @elements = []
5
- end
1
+ # DSA::Stack - Last-In-First-Out (LIFO) data structure.
2
+ #
3
+ # @example
4
+ # stack = DSA::Stack.new
5
+ # stack.push(1).push(2).push(3)
6
+ # stack.pop # => 3
7
+ # stack.peek # => 2
8
+ class DSA::Stack
9
+ # Initialize a new empty stack.
10
+ #
11
+ # @return [DSA::Stack] a new empty stack
12
+ def initialize
13
+ @elements = []
14
+ end
6
15
 
7
- def push(val)
8
- @elements.push(val)
9
- self
10
- end
16
+ # Pushes a value onto the top of the stack.
17
+ #
18
+ # @param val [Object] the value to push onto the stack
19
+ # @return [DSA::Stack] self for method chaining
20
+ # @example Push with chaining
21
+ # stack.push(1).push(2).push(3)
22
+ def push(val)
23
+ @elements.push(val)
24
+ self
25
+ end
11
26
 
12
- def pop
13
- raise IndexError, "stack is empty" if empty?
14
- @elements.pop
15
- end
27
+ # Removes and returns the value at the top of the stack.
28
+ #
29
+ # @return [Object] the value at the top of the stack
30
+ # @raise [IndexError] if the stack is empty
31
+ # @example
32
+ # stack.push(1).push(2)
33
+ # stack.pop # => 2
34
+ def pop
35
+ raise IndexError, "stack is empty" if empty?
36
+ @elements.pop
37
+ end
16
38
 
17
- def peek
18
- raise IndexError, "stack is empty" if empty?
19
- @elements.last
20
- end
39
+ # Returns the value at the top of the stack without removing it.
40
+ #
41
+ # @return [Object] the value at the top of the stack
42
+ # @raise [IndexError] if the stack is empty
43
+ # @example
44
+ # stack.push(1).push(2)
45
+ # stack.peek # => 2
46
+ def peek
47
+ raise IndexError, "stack is empty" if empty?
48
+ @elements.last
49
+ end
21
50
 
22
- def size
23
- @elements.size
24
- end
51
+ # Returns the number of elements in the stack.
52
+ #
53
+ # @return [Integer] the number of elements
54
+ # @example
55
+ # stack.push(1).push(2)
56
+ # stack.size # => 2
57
+ def size
58
+ @elements.size
59
+ end
25
60
 
26
- def empty?
27
- @elements.empty?
28
- end
61
+ # Checks if the stack is empty.
62
+ #
63
+ # @return [Boolean] true if the stack contains no elements
64
+ # @example
65
+ # stack = DSA::Stack.new
66
+ # stack.empty? # => true
67
+ def empty?
68
+ @elements.empty?
69
+ end
29
70
 
30
- def to_a
31
- @elements.dup
32
- end
71
+ # Returns a defensive copy of the stack as an array.
72
+ #
73
+ # @return [Array] an array containing all elements in the stack (top to bottom order)
74
+ # @example
75
+ # stack.push(1).push(2).push(3)
76
+ # stack.to_a # => [3, 2, 1]
77
+ def to_a
78
+ @elements.dup
33
79
  end
34
80
  end
data/lib/dsa-ruby/trie.rb CHANGED
@@ -1,70 +1,128 @@
1
- module DSA
2
- class Trie
3
- Node = Struct.new(:children, :word_end, keyword_init: true) do
4
- def initialize
5
- super(children: {}, word_end: false)
6
- end
7
- end
8
-
1
+ # DSA::Trie - A prefix tree (trie) data structure for string storage and lookup.
2
+ #
3
+ # Efficient for prefix-based searches and word lookups.
4
+ #
5
+ # @example
6
+ # trie = DSA::Trie.new
7
+ # trie.insert("apple")
8
+ # trie.search("apple") # => true
9
+ # trie.search("app") # => false
10
+ # trie.starts_with("app") # => true
11
+ # trie.delete("apple")
12
+ class DSA::Trie
13
+ # Internal node structure for the trie.
14
+ #
15
+ # @!attribute [rw] children
16
+ # @return [Hash] map of character to child node
17
+ # @!attribute [rw] word_end
18
+ # @return [Boolean] true if this node marks the end of a word
19
+ Node = Struct.new(:children, :word_end, keyword_init: true) do
9
20
  def initialize
10
- @root = Node.new
21
+ super(children: {}, word_end: false)
11
22
  end
23
+ end
12
24
 
13
- def insert(word)
14
- node = @root
15
- word.each_char do |char|
16
- node.children[char] ||= Node.new
17
- node = node.children[char]
18
- end
19
- node.word_end = true
20
- self
21
- end
25
+ # Initialize a new empty trie.
26
+ #
27
+ # @return [DSA::Trie] a new empty trie
28
+ def initialize
29
+ @root = Node.new
30
+ end
22
31
 
23
- def search(word)
24
- node = find_node(word)
25
- !!(node && node.word_end)
32
+ # Inserts a word into the trie.
33
+ #
34
+ # @param word [String] the word to insert
35
+ # @return [DSA::Trie] self for method chaining
36
+ # @example
37
+ # trie.insert("apple").insert("banana")
38
+ def insert(word)
39
+ node = @root
40
+ word.each_char do |char|
41
+ node.children[char] ||= Node.new
42
+ node = node.children[char]
26
43
  end
44
+ node.word_end = true
45
+ self
46
+ end
27
47
 
28
- def starts_with(prefix)
29
- !!find_node(prefix)
30
- end
48
+ # Searches for an exact word in the trie.
49
+ #
50
+ # @param word [String] the word to search for
51
+ # @return [Boolean] true if the word exists in the trie
52
+ # @example
53
+ # trie.insert("apple")
54
+ # trie.search("apple") # => true
55
+ # trie.search("app") # => false
56
+ def search(word)
57
+ node = find_node(word)
58
+ !!(node && node.word_end)
59
+ end
31
60
 
32
- def delete(word)
33
- delete_recursive(@root, word, 0)
34
- end
61
+ # Checks if any word in the trie starts with the given prefix.
62
+ #
63
+ # @param prefix [String] the prefix to search for
64
+ # @return [Boolean] true if any word starts with the prefix
65
+ # @example
66
+ # trie.insert("apple")
67
+ # trie.starts_with("app") # => true
68
+ # trie.starts_with("ban") # => false
69
+ def starts_with(prefix)
70
+ !!find_node(prefix)
71
+ end
35
72
 
36
- private
73
+ # Deletes a word from the trie if it exists.
74
+ #
75
+ # @param word [String] the word to delete
76
+ # @return [Boolean] true if the word was deleted, false if not found
77
+ # @example
78
+ # trie.insert("apple")
79
+ # trie.delete("apple") # => true
80
+ # trie.delete("banana") # => false
81
+ def delete(word)
82
+ delete_recursive(@root, word, 0)
83
+ end
37
84
 
38
- def find_node(word)
39
- node = @root
40
- word.each_char do |char|
41
- return nil unless node.children[char]
42
- node = node.children[char]
43
- end
44
- node
45
- end
85
+ private
46
86
 
47
- def delete_recursive(node, word, index)
48
- return false unless node
87
+ # Finds the node corresponding to a given word/prefix.
88
+ #
89
+ # @param word [String] the word or prefix to find
90
+ # @return [Node, nil] the node if found, nil otherwise
91
+ def find_node(word)
92
+ node = @root
93
+ word.each_char do |char|
94
+ return nil unless node.children[char]
95
+ node = node.children[char]
96
+ end
97
+ node
98
+ end
49
99
 
50
- if index == word.length
51
- return false unless node.word_end
100
+ # Recursively deletes a word from the trie.
101
+ #
102
+ # @param node [Node] the current node being examined
103
+ # @param word [String] the word to delete
104
+ # @param index [Integer] the current character index in the word
105
+ # @return [Boolean] true if the node should be deleted (no children and not word end)
106
+ def delete_recursive(node, word, index)
107
+ return false unless node
52
108
 
53
- node.word_end = false
54
- return node.children.empty?
55
- end
109
+ if index == word.length
110
+ return false unless node.word_end
56
111
 
57
- char = word[index]
58
- return false unless node.children[char]
112
+ node.word_end = false
113
+ return node.children.empty?
114
+ end
59
115
 
60
- should_delete = delete_recursive(node.children[char], word, index + 1)
116
+ char = word[index]
117
+ return false unless node.children[char]
61
118
 
62
- if should_delete
63
- node.children.delete(char)
64
- return !node.word_end && node.children.empty?
65
- end
119
+ should_delete = delete_recursive(node.children[char], word, index + 1)
66
120
 
67
- false
121
+ if should_delete
122
+ node.children.delete(char)
123
+ return !node.word_end && node.children.empty?
68
124
  end
125
+
126
+ false
69
127
  end
70
128
  end
@@ -1,50 +1,105 @@
1
- module DSA
2
- class UnionFind
3
- def initialize(n)
4
- @parent = (0...n).to_a
5
- @rank = Array.new(n, 0)
6
- @count = n
7
- end
8
-
9
- def find(x)
10
- raise IndexError, "index out of bounds" if x < 0 || x >= @parent.size
1
+ # DSA::UnionFind - Disjoint-set data structure with path compression and union by rank.
2
+ #
3
+ # Tracks a set of elements partitioned into disjoint subsets. Supports efficient
4
+ # union and find operations.
5
+ #
6
+ # @example
7
+ # uf = DSA::UnionFind.new(10)
8
+ # uf.union(1, 2)
9
+ # uf.union(2, 3)
10
+ # uf.connected?(1, 3) # => true
11
+ # uf.count # => 8 (started with 10, merged 2 pairs)
12
+ class DSA::UnionFind
13
+ # Initialize a new UnionFind structure with n disjoint sets.
14
+ #
15
+ # @param n [Integer] the number of elements (0 to n-1), each in its own set
16
+ # @return [DSA::UnionFind] a new UnionFind structure
17
+ # @example
18
+ # uf = DSA::UnionFind.new(10)
19
+ def initialize(n)
20
+ @parent = (0...n).to_a
21
+ @rank = Array.new(n, 0)
22
+ @count = n
23
+ end
11
24
 
12
- @parent[x] = find(@parent[x]) if @parent[x] != x
13
- @parent[x]
14
- end
25
+ # Finds the root of the set containing element x, with path compression.
26
+ #
27
+ # @param x [Integer] the element to find
28
+ # @return [Integer] the root of the set containing x
29
+ # @raise [IndexError] if x is out of bounds
30
+ # @example
31
+ # uf.union(1, 2)
32
+ # uf.find(2) # => root of the set containing 1 and 2
33
+ def find(x)
34
+ raise IndexError, "index out of bounds" if x < 0 || x >= @parent.size
15
35
 
16
- def union(x, y)
17
- raise IndexError, "index out of bounds" if x < 0 || x >= @parent.size
18
- raise IndexError, "index out of bounds" if y < 0 || y >= @parent.size
36
+ @parent[x] = find(@parent[x]) if @parent[x] != x
37
+ @parent[x]
38
+ end
19
39
 
20
- root_x = find(x)
21
- root_y = find(y)
40
+ # Merges the sets containing elements x and y.
41
+ #
42
+ # @param x [Integer] the first element
43
+ # @param y [Integer] the second element
44
+ # @return [Boolean] true if the sets were merged, false if already connected
45
+ # @raise [IndexError] if x or y is out of bounds
46
+ # @example
47
+ # uf.union(1, 2) # => true
48
+ # uf.union(2, 3) # => true
49
+ # uf.union(1, 3) # => false (already connected)
50
+ def union(x, y)
51
+ raise IndexError, "index out of bounds" if x < 0 || x >= @parent.size
52
+ raise IndexError, "index out of bounds" if y < 0 || y >= @parent.size
22
53
 
23
- return false if root_x == root_y
54
+ root_x = find(x)
55
+ root_y = find(y)
24
56
 
25
- if @rank[root_x] < @rank[root_y]
26
- @parent[root_x] = root_y
27
- elsif @rank[root_x] > @rank[root_y]
28
- @parent[root_y] = root_x
29
- else
30
- @parent[root_y] = root_x
31
- @rank[root_x] += 1
32
- end
57
+ return false if root_x == root_y
33
58
 
34
- @count -= 1
35
- true
59
+ if @rank[root_x] < @rank[root_y]
60
+ @parent[root_x] = root_y
61
+ elsif @rank[root_x] > @rank[root_y]
62
+ @parent[root_y] = root_x
63
+ else
64
+ @parent[root_y] = root_x
65
+ @rank[root_x] += 1
36
66
  end
37
67
 
38
- def connected?(x, y)
39
- find(x) == find(y)
40
- end
68
+ @count -= 1
69
+ true
70
+ end
41
71
 
42
- def count
43
- @count
44
- end
72
+ # Checks if two elements are in the same set.
73
+ #
74
+ # @param x [Integer] the first element
75
+ # @param y [Integer] the second element
76
+ # @return [Boolean] true if x and y are in the same set
77
+ # @example
78
+ # uf.union(1, 2)
79
+ # uf.connected?(1, 2) # => true
80
+ # uf.connected?(1, 3) # => false
81
+ def connected?(x, y)
82
+ find(x) == find(y)
83
+ end
45
84
 
46
- def size
47
- @parent.size
48
- end
85
+ # Returns the number of disjoint sets.
86
+ #
87
+ # @return [Integer] the number of disjoint sets
88
+ # @example
89
+ # uf = DSA::UnionFind.new(10)
90
+ # uf.union(1, 2)
91
+ # uf.count # => 9
92
+ def count
93
+ @count
94
+ end
95
+
96
+ # Returns the total number of elements.
97
+ #
98
+ # @return [Integer] the number of elements (0 to n-1)
99
+ # @example
100
+ # uf = DSA::UnionFind.new(10)
101
+ # uf.size # => 10
102
+ def size
103
+ @parent.size
49
104
  end
50
105
  end
@@ -1,3 +1,10 @@
1
+ # DSA module - Data Structures for Algorithms in Ruby
2
+ #
3
+ # Provides common data structures needed for coding interviews and algorithm
4
+ # problems that are missing from Ruby's standard library.
1
5
  module DSA
2
- VERSION = "1.0.0"
6
+ # Current version of the dsa-ruby gem.
7
+ #
8
+ # @return [String] the version string (semantic versioning)
9
+ VERSION = "1.0.2"
3
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dsa-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - You
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '3.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: yard
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.9'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.9'
26
40
  description: Provides MinHeap, MaxHeap, PriorityQueue, Deque, Trie, UnionFind, and
27
41
  LinkedList — data structures commonly needed for coding interviews but missing from
28
42
  Ruby's stdlib.