From 3902bef7a6baf67eb144e8edc281d5e18227a2c6 Mon Sep 17 00:00:00 2001 From: Amos Paribocci Date: Mon, 22 May 2023 08:13:05 +0200 Subject: [PATCH] Adding unweighted graph data structure --- data_structures/graphs/unweighted_graph.rb | 62 ++++++++++++++ .../graphs/unweighted_graph_test.rb | 82 +++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 data_structures/graphs/unweighted_graph.rb create mode 100644 data_structures/graphs/unweighted_graph_test.rb diff --git a/data_structures/graphs/unweighted_graph.rb b/data_structures/graphs/unweighted_graph.rb new file mode 100644 index 0000000..8da857a --- /dev/null +++ b/data_structures/graphs/unweighted_graph.rb @@ -0,0 +1,62 @@ +require 'set' + +## +# This class aims to represent unweighted graphs +# (i.e. graphs for which edges between nodes have no specific weight associated to them). +# +# Both directed (i.e. an edge between node U and node V does not imply an edge in the opposite direction) +# and undirected graphs are supported, depending on the constructor invocation. + +class UnweightedGraph + attr_reader :nodes + attr_reader :directed + + def initialize(nodes: [], neighbors: {}, directed: true) + @nodes = Set[] + @neighbors = {} + @directed = directed + for node in nodes + add_node(node) + end + neighbors.each do |node, neighbors| + for neighbor in neighbors + add_edge(node, neighbor) + end + end + end + + def add_node(node) + if include?(node) + raise ArgumentError, "node #{node} already exists in this graph!" + end + @nodes.add(node) + @neighbors[node] = Set[] + end + + def add_edge(start_node, end_node) + if has_neighbor?(start_node, end_node) + raise ArgumentError, "node #{start_node} already has an edge to #{end_node} in this graph!" + end + @neighbors[start_node].add(end_node) + @neighbors[end_node].add(start_node) unless directed + end + + def neighbors(node) + unless include?(node) + raise ArgumentError, "node #{node} does not exist in this graph!" + end + @neighbors[node] + end + + def empty? + nodes.empty? + end + + def include?(node) + nodes.include?(node) + end + + def has_neighbor?(start_node, end_node) + neighbors(start_node).include?(end_node) + end +end diff --git a/data_structures/graphs/unweighted_graph_test.rb b/data_structures/graphs/unweighted_graph_test.rb new file mode 100644 index 0000000..b4f7443 --- /dev/null +++ b/data_structures/graphs/unweighted_graph_test.rb @@ -0,0 +1,82 @@ +require 'minitest/autorun' +require 'set' +require_relative 'unweighted_graph' + +class TestUnweightedGraph < Minitest::Test + def test_directed_unweighted_graph_creation + graph = UnweightedGraph.new(nodes: [:u, :v, :w], neighbors: {:u => [:v]}, directed: true) + + assert graph.nodes.to_set == Set[:u, :v, :w] + assert graph.neighbors(:u).to_set == Set[:v] + assert graph.neighbors(:v).empty? + assert graph.neighbors(:w).empty? + end + + def test_undirected_unweighted_graph_creation + graph = UnweightedGraph.new(nodes: [:u, :v, :w], neighbors: {:u => [:v]}, directed: false) + + assert graph.nodes.to_set == Set[:u, :v, :w] + assert graph.neighbors(:u).to_set == Set[:v] + assert graph.neighbors(:v).to_set == Set[:u] + assert graph.neighbors(:w).empty? + end + + def test_empty_returns_true_for_empty_graph + graph = UnweightedGraph.new + + assert graph.empty? + end + + def test_empty_returns_false_for_non_empty_graph + graph = UnweightedGraph.new(nodes: [:u]) + + assert !graph.empty? + end + + def test_include_returns_true_for_graph_nodes + graph = UnweightedGraph.new(nodes: [:u]) + + assert graph.include?(:u) + end + + def test_include_returns_false_for_non_graph_nodes + graph = UnweightedGraph.new + + assert !graph.include?(:u) + end + + def test_has_neighbor_returns_true_for_graph_node_neighbors + graph = UnweightedGraph.new(nodes: [:u, :v], neighbors: {:u => [:v]}) + + assert graph.has_neighbor?(:u, :v) + end + + def test_has_neighbor_returns_false_for_non_graph_node_neighbors + graph = UnweightedGraph.new(nodes: [:u, :v]) + + assert !graph.has_neighbor?(:u, :v) + end + + def test_add_node_adds_node_to_graph + graph = UnweightedGraph.new + graph.add_node(:u) + + assert graph.nodes.to_set == Set[:u] + end + + def test_add_edge_adds_edge_to_directed_unweighted_graph + graph = UnweightedGraph.new(nodes: [:u, :v], directed: true) + graph.add_edge(:u, :v) + + assert graph.neighbors(:u).to_set == Set[:v] + assert graph.neighbors(:v).empty? + end + + def test_add_edge_adds_edge_to_directed_unweighted_graph + graph = UnweightedGraph.new(nodes: [:u, :v], directed: false) + graph.add_edge(:u, :v) + + assert graph.neighbors(:u).to_set == Set[:v] + assert graph.neighbors(:v).to_set == Set[:u] + end +end