lib/union_find.rb in union_find-0.0.1 vs lib/union_find.rb in union_find-0.0.2
- old
+ new
@@ -10,11 +10,11 @@
# returns the total number of components.
# This implementation uses weighted quick union by rank with path compression
# by halving.
-# Initializing a data structure with number_of_components sites takes linear time.
+# Initializing a data structure takes linear time.
# Afterwards, the union, find, and connected
# operations take logarithmic time (in the worst case) and the
# count operation takes constant time.
# @author Robert Sedgewick
@@ -22,62 +22,61 @@
# @author Michael Imstepf
# @see http://algs4.cs.princeton.edu/15uf/UF.java.html
# @see http://algs4.cs.princeton.edu/15uf/
class UnionFind
+ attr_accessor :components
+
# Initializes an empty union-find data structure with
# n isolated components 0 through n-1.
# @param components [Array] components
# @raise [ArgumentError] if components.length < 1 or if components is not an Array
def initialize(components)
- raise ArgumentError, 'input is not an Array' unless components.class == Array
+ # Unexpected behaviour,
+ # for Sets the @components does not copy the content of components
+ # rather it points to the components variable and any changes to the components
+ # variables are reflected in @components.
+ # Force copy instead.
+ @components = components.dup
- components = components.uniq # remove duplicates
- @number_of_isolated_components = components.length
-
+ raise ArgumentError, 'input is not a Set' unless @components.is_a? Set
+
+ @number_of_isolated_components = @components.length
+
raise ArgumentError, 'number of components is < 1' if @number_of_isolated_components < 1
- @parent = {} # parent of i
- @tree_size = {} # size of tree rooted at i (cannot be more than 31)
- components.each do |component|
- @parent[component] = component
- @tree_size[component] = 1
+ @parent = {} # parent of component
+ @tree_size = {} # size of tree rooted at component (cannot be more than 31)
+ end
+
+ # Dynamically adds component.
+ # @return [Component] component
+ def add(component)
+ if @components.add?(component)
+ # if component does not already exist
+ @number_of_isolated_components += 1
end
end
# Returns the number of isolated components.
# @return [Interger] the number of components
def count_isolated_components
@number_of_isolated_components
end
- # Returns the root of a component.
- # @param component_id [Integer] the integer representing one component
- # @return [Component] the root of the component
- # @raise [IndexError] unless component exists
- def find_root(component)
- raise IndexError, 'component does not exist' unless @parent[component]
-
- while component != @parent[component] # stop at the top node where component id == parent id
- @parent[component] = @parent[@parent[component]] # path compression by halving
- component = @parent[component]
- end
-
- return component
- end
-
# Connect root of component 1 with root of component 2
# by attaching smaller subtree root node with larger tree.
# If both trees have the same size, the root of the second
# component becomes a child of the root of the first component.
- # @param component_1_id [Integer] the integer representing one component
- # @param component_2_id [Integer] the integer representing the other component
+ # @param component_1 [Component] component
+ # @param component_2 [Component] component
# @return [Component, NilClass] the root of the larger tree or the root of the first component if both have the same tree size or nil if no connection has been made
def union(component_1, component_2)
root_component_1 = find_root(component_1)
root_component_2 = find_root(component_2)
+ # exit if already connected
return nil if root_component_1 == root_component_2
# make smaller root point to larger one
if @tree_size[root_component_1] < @tree_size[root_component_2]
@parent[root_component_1] = root_component_2
@@ -93,14 +92,44 @@
root
end
# Do two components share the same root?
- # @param component_1 [Integer] the integer representing one component
- # @param component_2 [Integer] the integer representing the other component
+ # @param component_1 [Component] component
+ # @param component_2 [Component] component
# @return [Boolean]
def connected?(component_1, component_2)
find_root(component_1) == find_root(component_2)
end
+
+ private
+
+ # Returns the root of a component.
+ # @param component [Component] component
+ # @return [Component] the root of the component
+ # @raise [IndexError] unless component exists
+ def find_root(component)
+ raise IndexError, 'component does not exist' unless @components.include? component
+
+ set_parent_and_tree_size(component)
+
+ while component != @parent[component] # stop at the top node where component id == parent id
+ @parent[component] = @parent[@parent[component]] # path compression by halving
+ component = @parent[component]
+ end
+
+ return component
+ end
+
+ # Sets parent and tree size for each component.
+ # To prevent initialize method from taking linear time,
+ # this method is used to do this assignment on demand.
+ # @param component [Component] component
+ # @return [Component] component
+ def set_parent_and_tree_size(component)
+ @parent[component] ||= component
+ @tree_size[component] ||= 1
+ component
+ end
end
end
\ No newline at end of file