Added undirected graph management
parent
9b5259c192
commit
726d2b923f
@ -0,0 +1,512 @@
|
||||
// Package graph is an undirected graph management library.
|
||||
// Do not support multithreaded updates.
|
||||
package graph
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"gitea.paas.celticinfo.fr/oabrivard/abrolgo/algo"
|
||||
"gitea.paas.celticinfo.fr/oabrivard/abrolgo/container"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
type Edge[T comparable, V constraints.Integer] struct {
|
||||
ID int
|
||||
src T
|
||||
dest T
|
||||
weight V
|
||||
}
|
||||
|
||||
type Graph[T comparable, V constraints.Integer] struct {
|
||||
edgeID int
|
||||
v, e int // number of edges and vertices
|
||||
adjacencyList map[T][]Edge[T, V]
|
||||
edges []Edge[T, V]
|
||||
}
|
||||
|
||||
// NewGraph creates a new instance of the undirected Graph data structure.
|
||||
// It initializes the graph with zero vertices and zero edges.
|
||||
// The graph uses a map to store the adjacency list and a slice to store the edges.
|
||||
// The type parameters T and V represent the types of the vertices and edge values, respectively.
|
||||
// The vertices must be comparable, and the edge values must be integers.
|
||||
// Returns a pointer to the newly created Graph.
|
||||
func NewGraph[T comparable, V constraints.Integer]() *Graph[T, V] {
|
||||
return &Graph[T, V]{
|
||||
edgeID: 0,
|
||||
v: 0,
|
||||
e: 0,
|
||||
adjacencyList: map[T][]Edge[T, V]{},
|
||||
edges: []Edge[T, V]{},
|
||||
}
|
||||
}
|
||||
|
||||
// AddVertex adds a new vertex to the graph.
|
||||
// This function initializes an empty adjacency list for the new vertex.
|
||||
func (g *Graph[T, V]) AddVertex(vertex T) {
|
||||
g.adjacencyList[vertex] = []Edge[T, V]{}
|
||||
}
|
||||
|
||||
// AddEdge adds an edge to the graph between the source node (src) and the destination node (dest),
|
||||
// with the given distance (dist).
|
||||
// It adds the edge in both directions (src -> dest and dest -> src).
|
||||
// If vertices src and dest do not exist in the graph, they are added to the graph.
|
||||
func (g *Graph[T, V]) AddEdge(src, dest T, dist V) {
|
||||
g.edgeID++
|
||||
srcToDest := Edge[T, V]{g.edgeID, src, dest, dist}
|
||||
destToSrc := Edge[T, V]{g.edgeID, dest, src, dist}
|
||||
g.adjacencyList[src] = append(g.adjacencyList[src], srcToDest)
|
||||
g.adjacencyList[dest] = append(g.adjacencyList[dest], destToSrc)
|
||||
g.edges = append(g.edges, srcToDest)
|
||||
g.e++
|
||||
g.v = len(g.adjacencyList)
|
||||
}
|
||||
|
||||
// RemoveEdge removes the first edge between the source node (src) and the destination node (dest).
|
||||
// It updates the adjacency list, the list of edges, and the number of edges in the graph.
|
||||
// If the edge does not exist, it does nothing.
|
||||
func (g *Graph[T, V]) RemoveEdge(src, dest T) {
|
||||
var ID int
|
||||
edgeFound := false
|
||||
|
||||
// Check if source vertex exists
|
||||
if _, exists := g.adjacencyList[src]; !exists {
|
||||
// Optionally handle the case where the source vertex does not exist
|
||||
return
|
||||
}
|
||||
|
||||
// Remove the edge from the adjacency list
|
||||
for i, edge := range g.adjacencyList[src] {
|
||||
if edge.dest == dest {
|
||||
g.adjacencyList[src] = append(g.adjacencyList[src][:i], g.adjacencyList[src][i+1:]...)
|
||||
ID = edge.ID
|
||||
edgeFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the reciprocal edge
|
||||
for i, edge := range g.adjacencyList[dest] {
|
||||
if edge.ID == ID {
|
||||
g.adjacencyList[dest] = append(g.adjacencyList[dest][:i], g.adjacencyList[dest][i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If no edge was removed from the adjacency list, return to avoid the uneceesary loop
|
||||
if !edgeFound {
|
||||
return
|
||||
}
|
||||
|
||||
// Remove the edge from the list of edges
|
||||
for i, edge := range g.edges {
|
||||
if edge.ID == ID {
|
||||
g.edges = append(g.edges[:i], g.edges[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
g.e--
|
||||
}
|
||||
|
||||
// RemoveEdge removes the first edge between the source node (src) and the destination node (dest).
|
||||
// It updates the adjacency list, the list of edges, and the number of edges in the graph.
|
||||
// If the edge does not exist, it does nothing.
|
||||
func (g *Graph[T, V]) RemoveEdgeWithID(ID int) {
|
||||
edgeFound := false
|
||||
var edgeToRemove Edge[T, V]
|
||||
|
||||
// Remove the edge from the list of edges
|
||||
for i, edge := range g.edges {
|
||||
if edge.ID == ID {
|
||||
edgeFound = true
|
||||
edgeToRemove = edge
|
||||
g.edges = append(g.edges[:i], g.edges[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Return if no edge found
|
||||
if !edgeFound {
|
||||
return
|
||||
}
|
||||
|
||||
src := edgeToRemove.src
|
||||
dest := edgeToRemove.dest
|
||||
|
||||
// Remove the edge from the adjacency list
|
||||
for i, edge := range g.adjacencyList[src] {
|
||||
if edge.ID == ID {
|
||||
g.adjacencyList[src] = append(g.adjacencyList[src][:i], g.adjacencyList[src][i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the reciprocal edge
|
||||
for i, edge := range g.adjacencyList[dest] {
|
||||
if edge.ID == ID {
|
||||
g.adjacencyList[dest] = append(g.adjacencyList[dest][:i], g.adjacencyList[dest][i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
g.e--
|
||||
}
|
||||
|
||||
// RemoveVertex removes the specified vertex from the graph.
|
||||
// It updates the adjacency list, the list of edges, and the number of edges and vertices in the graph.
|
||||
// If the vertex does not exist, it does nothing.
|
||||
func (g *Graph[T, V]) RemoveVertex(vertex T) {
|
||||
// Remove the vertex from the adjacency list
|
||||
delete(g.adjacencyList, vertex)
|
||||
|
||||
// Remove all edges in the adjacency list that have 'vertex' as their destination
|
||||
for src, edges := range g.adjacencyList {
|
||||
for i := 0; i < len(edges); {
|
||||
if edges[i].dest == vertex {
|
||||
// Remove edge from the slice
|
||||
edges = append(edges[:i], edges[i+1:]...)
|
||||
g.e-- // Decrement the edge count
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
g.adjacencyList[src] = edges
|
||||
}
|
||||
|
||||
// Remove all edges from the edges slice that involve 'vertex'
|
||||
for i := 0; i < len(g.edges); {
|
||||
if g.edges[i].src == vertex || g.edges[i].dest == vertex {
|
||||
// Remove edge from the slice
|
||||
g.edges = append(g.edges[:i], g.edges[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
g.v = len(g.adjacencyList)
|
||||
}
|
||||
|
||||
// GetVertices returns the list of vertices in the graph.
|
||||
func (g *Graph[T, V]) GetVertices() []T {
|
||||
vertices := make([]T, len(g.adjacencyList))
|
||||
i := 0
|
||||
for vertex := range g.adjacencyList {
|
||||
vertices[i] = vertex
|
||||
i++
|
||||
}
|
||||
return vertices
|
||||
}
|
||||
|
||||
// GetEdges returns the list of edges in the graph.
|
||||
func (g *Graph[T, V]) GetEdges() []Edge[T, V] {
|
||||
return g.edges
|
||||
}
|
||||
|
||||
// GetNeighbors returns the list of edges connected to the specified vertex in the graph.
|
||||
func (g *Graph[T, V]) GetNeighbors(vertex T) []Edge[T, V] {
|
||||
return g.adjacencyList[vertex]
|
||||
}
|
||||
|
||||
// HasNeighbor checks if a given source node has a neighbor node in the graph.
|
||||
func (g *Graph[T, V]) HasNeighbor(src, neighbor T) bool {
|
||||
return slices.ContainsFunc(g.adjacencyList[src], func(edge Edge[T, V]) bool {
|
||||
return edge.dest == neighbor
|
||||
})
|
||||
}
|
||||
|
||||
// GetEdge returns the first edge between the source node (src) and the destination node (dest).
|
||||
// If the edge does not exist, it returns an empty Edge.
|
||||
func (g *Graph[T, V]) GetEdge(src, dest T) Edge[T, V] {
|
||||
for _, edge := range g.adjacencyList[src] {
|
||||
if edge.dest == dest {
|
||||
return edge
|
||||
}
|
||||
}
|
||||
return Edge[T, V]{}
|
||||
}
|
||||
|
||||
// String returns a string representation of the graph.
|
||||
func (g *Graph[T, V]) String() string {
|
||||
var sb strings.Builder
|
||||
sb.Grow(512) // Provide an initial capacity estimate
|
||||
|
||||
for vertex, edges := range g.adjacencyList {
|
||||
sb.WriteString(fmt.Sprint(vertex))
|
||||
sb.WriteString(": ")
|
||||
|
||||
edgeStrings := make([]string, len(edges))
|
||||
for i, e := range edges {
|
||||
edgeStrings[i] = fmt.Sprintf("(%v, %v, %v, %v)", e.ID, e.src, e.dest, e.weight)
|
||||
}
|
||||
|
||||
sb.WriteString(strings.Join(edgeStrings, " "))
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// CountComponents returns the number of connected components in the graph.
|
||||
func (g *Graph[T, V]) CountComponents() int {
|
||||
visited := map[T]bool{}
|
||||
count := 0
|
||||
|
||||
// We perform a breadth-first search starting from each unvisited vertex
|
||||
// and increment the count for each new component found.
|
||||
for vertex := range g.adjacencyList {
|
||||
if !visited[vertex] {
|
||||
count++
|
||||
queue := container.NewQueue[T]()
|
||||
queue.Enqueue(vertex)
|
||||
|
||||
for queue.HasElement() {
|
||||
curVert := queue.Dequeue()
|
||||
visited[curVert] = true
|
||||
|
||||
for _, linkedNode := range g.adjacencyList[curVert] {
|
||||
if !visited[linkedNode.dest] {
|
||||
queue.Enqueue(linkedNode.dest)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// IsConnected returns true if the graph is connected, false otherwise.
|
||||
func (g *Graph[T, V]) IsConnected() bool {
|
||||
return g.CountComponents() == 1
|
||||
}
|
||||
|
||||
// IsCyclic returns true if the graph contains a cycle, false otherwise.
|
||||
func (g *Graph[T, V]) IsCyclic() bool {
|
||||
visited := map[T]bool{}
|
||||
recStack := map[T]bool{}
|
||||
edgeIDs := map[int]bool{}
|
||||
|
||||
for vertex := range g.adjacencyList {
|
||||
if g.isCyclicUtil(vertex, visited, recStack, edgeIDs) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isCyclicUtil is a helper function for IsCyclic.
|
||||
// It performs a depth-first search on the graph starting from the specified vertex.
|
||||
// It returns true if the graph contains a cycle, false otherwise.
|
||||
func (g *Graph[T, V]) isCyclicUtil(vertex T, visited, recStack map[T]bool, edgeIDs map[int]bool) bool {
|
||||
visited[vertex] = true
|
||||
recStack[vertex] = true
|
||||
|
||||
for _, neighbor := range g.adjacencyList[vertex] {
|
||||
// If the edge has already been visited via a reciprocal link, skip it
|
||||
if edgeIDs[neighbor.ID] {
|
||||
continue
|
||||
}
|
||||
edgeIDs[neighbor.ID] = true
|
||||
|
||||
if !visited[neighbor.dest] && g.isCyclicUtil(neighbor.dest, visited, recStack, edgeIDs) {
|
||||
return true
|
||||
} else if recStack[neighbor.dest] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
recStack[vertex] = false
|
||||
return false
|
||||
}
|
||||
|
||||
// IsTree returns true if the graph is a tree, false otherwise.
|
||||
func (g *Graph[T, V]) IsTree() bool {
|
||||
return g.IsConnected() && !g.IsCyclic()
|
||||
}
|
||||
|
||||
// IsBipartite returns true if the graph is bipartite, false otherwise.
|
||||
func (g *Graph[T, V]) IsBipartite() bool {
|
||||
visited := map[T]bool{}
|
||||
colors := map[T]bool{}
|
||||
edgeIDs := map[int]bool{}
|
||||
|
||||
for vertex := range g.adjacencyList {
|
||||
if !visited[vertex] {
|
||||
if !g.isBipartiteUtil(vertex, visited, colors, edgeIDs) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isBipartiteUtil is a helper function for IsBipartite.
|
||||
// It performs a depth-first search on the graph starting from the specified vertex.
|
||||
// It returns true if the graph is bipartite, false otherwise.
|
||||
func (g *Graph[T, V]) isBipartiteUtil(vertex T, visited, colors map[T]bool, edgeIDs map[int]bool) bool {
|
||||
visited[vertex] = true
|
||||
|
||||
for _, neighbor := range g.adjacencyList[vertex] {
|
||||
// If the edge has already been visited via a reciprocal link, skip it
|
||||
if edgeIDs[neighbor.ID] {
|
||||
continue
|
||||
}
|
||||
edgeIDs[neighbor.ID] = true
|
||||
|
||||
if !visited[neighbor.dest] {
|
||||
colors[neighbor.dest] = !colors[vertex]
|
||||
if !g.isBipartiteUtil(neighbor.dest, visited, colors, edgeIDs) {
|
||||
return false
|
||||
}
|
||||
} else if colors[vertex] == colors[neighbor.dest] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// kargerMinCutUF performs the Karger's algorithm to find the minimum cut in the graph using the Union-Find data structure.
|
||||
// It returns the number of cut edges and the subsets of vertices after the contraction.
|
||||
// Being a monte-carlo algorithm, it can return a wrong result with a very small probability.
|
||||
// Adapted from https://www.geeksforgeeks.org/introduction-and-implementation-of-kargers-algorithm-for-minimum-cut/?ref=lbp
|
||||
func (g *Graph[T, V]) kargerMinCutUF(loops int) (int, [][]T) {
|
||||
var subsets map[T]*algo.Subset[T]
|
||||
minCut := math.MaxInt64
|
||||
|
||||
for loops > 0 {
|
||||
loops--
|
||||
|
||||
// Allocate memory for creating V subsets.
|
||||
subsets = map[T]*algo.Subset[T]{}
|
||||
|
||||
// Get data of given graph
|
||||
edges := g.edges
|
||||
|
||||
// Create V subsets with single elements
|
||||
for k := range g.adjacencyList {
|
||||
subsets[k] = &algo.Subset[T]{Parent: k, Rank: 0}
|
||||
}
|
||||
|
||||
// Initially there are V vertices in
|
||||
// contracted graph
|
||||
vertices := g.v
|
||||
|
||||
// Keep contracting vertices until there are
|
||||
// 2 vertices.
|
||||
for vertices > 2 {
|
||||
// Pick a random edge
|
||||
i := rand.Int() % g.e
|
||||
|
||||
// Find vertices (or sets) of two corners
|
||||
// of current edge
|
||||
subset1 := algo.Find(subsets, edges[i].src)
|
||||
subset2 := algo.Find(subsets, edges[i].dest)
|
||||
|
||||
// If two corners belong to same subset,
|
||||
// then no point considering this edge
|
||||
if subset1 == subset2 {
|
||||
continue
|
||||
} else {
|
||||
// Else contract the edge (or combine the
|
||||
// corners of edge into one vertex)
|
||||
vertices--
|
||||
algo.Union(subsets, subset1, subset2)
|
||||
}
|
||||
}
|
||||
|
||||
// Now we have two vertices (or subsets) left in
|
||||
// the contracted graph, so count the edges between
|
||||
// two components and return the count.
|
||||
cutedges := 0
|
||||
for i := 0; i < g.e; i++ {
|
||||
subset1 := algo.Find(subsets, edges[i].src)
|
||||
subset2 := algo.Find(subsets, edges[i].dest)
|
||||
if subset1 != subset2 {
|
||||
cutedges++
|
||||
}
|
||||
}
|
||||
|
||||
if cutedges < minCut {
|
||||
minCut = cutedges
|
||||
}
|
||||
}
|
||||
|
||||
// Consolidate the subsets
|
||||
consolidatedSubsets := map[T][]T{}
|
||||
for k, v := range subsets {
|
||||
_, found := consolidatedSubsets[v.Parent]
|
||||
if !found {
|
||||
consolidatedSubsets[v.Parent] = []T{}
|
||||
}
|
||||
consolidatedSubsets[v.Parent] = append(consolidatedSubsets[v.Parent], k)
|
||||
}
|
||||
|
||||
resultSubsets := [][]T{}
|
||||
for _, s := range consolidatedSubsets {
|
||||
resultSubsets = append(resultSubsets, s)
|
||||
}
|
||||
|
||||
return minCut, resultSubsets
|
||||
}
|
||||
|
||||
// DFS performs a depth-first search starting from the specified vertex until a goal vertex is reached.
|
||||
// It returns a slice of vertices visited during the search.
|
||||
// If no goal vertex is found, an empty slice is returned.
|
||||
func (g *Graph[T, V]) DFS(start T, isGoal func(vertex T) bool) []T {
|
||||
visited := map[T]bool{}
|
||||
result := []T{}
|
||||
stack := container.NewStack[T]()
|
||||
|
||||
stack.Push(start)
|
||||
|
||||
for stack.HasElement() {
|
||||
curVert := stack.Pop()
|
||||
result = append(result, curVert)
|
||||
|
||||
if isGoal(curVert) {
|
||||
return result
|
||||
}
|
||||
|
||||
if !visited[curVert] {
|
||||
visited[curVert] = true
|
||||
|
||||
for _, linkedNode := range g.adjacencyList[curVert] {
|
||||
stack.Push(linkedNode.dest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return []T{}
|
||||
}
|
||||
|
||||
// BFS performs a breadth-first search starting from the specified vertex until a goal vertex is reached.
|
||||
// It returns a slice of vertices visited during the search.
|
||||
// If no goal vertex is found, an empty slice is returned.
|
||||
func (g *Graph[T, V]) BFS(start T, isGoal func(vertex T) bool) []T {
|
||||
visited := map[T]bool{}
|
||||
result := []T{}
|
||||
queue := container.NewQueue[T]()
|
||||
|
||||
visited[start] = true
|
||||
queue.Enqueue(start)
|
||||
|
||||
for queue.HasElement() {
|
||||
curVert := queue.Dequeue()
|
||||
result = append(result, curVert)
|
||||
|
||||
if isGoal(curVert) {
|
||||
return result
|
||||
}
|
||||
|
||||
for _, linkedNode := range g.adjacencyList[curVert] {
|
||||
if !visited[linkedNode.dest] {
|
||||
visited[linkedNode.dest] = true
|
||||
queue.Enqueue(linkedNode.dest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return []T{}
|
||||
}
|
||||
@ -0,0 +1,724 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewGraph(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Check if the graph is initialized correctly
|
||||
if g.v != 0 {
|
||||
t.Errorf("NewGraph() failed: expected v = 0, got v = %d", g.v)
|
||||
}
|
||||
if g.e != 0 {
|
||||
t.Errorf("NewGraph() failed: expected e = 0, got e = %d", g.e)
|
||||
}
|
||||
if len(g.adjacencyList) != 0 {
|
||||
t.Errorf("NewGraph() failed: expected empty adjacency list, got %v", g.adjacencyList)
|
||||
}
|
||||
if len(g.edges) != 0 {
|
||||
t.Errorf("NewGraph() failed: expected empty edges list, got %v", g.edges)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddEdge(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add edges to the graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 4, 30)
|
||||
|
||||
// Check if the edges are added correctly
|
||||
expectedV := 4
|
||||
if g.v != expectedV {
|
||||
t.Errorf("AddEdge() failed: expected v = %d, got v = %d", expectedV, g.v)
|
||||
}
|
||||
|
||||
expectedE := 3
|
||||
if g.e != expectedE {
|
||||
t.Errorf("AddEdge() failed: expected e = %d, got e = %d", expectedE, g.e)
|
||||
}
|
||||
|
||||
expectedAdjacencyList := map[int][]Edge[int, int]{
|
||||
1: {{1, 1, 2, 10}},
|
||||
2: {{1, 2, 1, 10}, {2, 2, 3, 20}},
|
||||
3: {{2, 3, 2, 20}, {3, 3, 4, 30}},
|
||||
4: {{3, 4, 3, 30}},
|
||||
}
|
||||
if !compareAdjacencyLists(g.adjacencyList, expectedAdjacencyList) {
|
||||
t.Errorf("AddEdge() failed: expected adjacency list = %v, got %v", expectedAdjacencyList, g.adjacencyList)
|
||||
}
|
||||
|
||||
expectedEdges := []Edge[int, int]{
|
||||
{1, 1, 2, 10},
|
||||
{2, 2, 3, 20},
|
||||
{3, 3, 4, 30},
|
||||
}
|
||||
if !compareEdges(g.edges, expectedEdges) {
|
||||
t.Errorf("AddEdge() failed: expected edges list = %v, got %v", expectedEdges, g.edges)
|
||||
}
|
||||
}
|
||||
|
||||
func compareAdjacencyLists(a, b map[int][]Edge[int, int]) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for key, valueA := range a {
|
||||
valueB, ok := b[key]
|
||||
if !ok || !compareEdges(valueA, valueB) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func compareEdges(a, b []Edge[int, int]) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func TestGetNeighbors(t *testing.T) {
|
||||
// Create a new graph
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add vertices and edges to the graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(1, 3, 20)
|
||||
g.AddEdge(2, 3, 30)
|
||||
|
||||
// Test GetNeighbors for vertex 1
|
||||
neighbors := g.GetNeighbors(1)
|
||||
expectedNeighbors := []Edge[int, int]{{ID: 1, src: 1, dest: 2, weight: 10}, {ID: 2, src: 1, dest: 3, weight: 20}}
|
||||
if !equalEdges(neighbors, expectedNeighbors) {
|
||||
t.Errorf("GetNeighbors() failed for vertex 1: expected %v, got %v", expectedNeighbors, neighbors)
|
||||
}
|
||||
|
||||
// Test GetNeighbors for vertex 2
|
||||
neighbors = g.GetNeighbors(2)
|
||||
expectedNeighbors = []Edge[int, int]{{ID: 1, src: 2, dest: 1, weight: 10}, {ID: 3, src: 2, dest: 3, weight: 30}}
|
||||
if !equalEdges(neighbors, expectedNeighbors) {
|
||||
t.Errorf("GetNeighbors() failed for vertex 2: expected %v, got %v", expectedNeighbors, neighbors)
|
||||
}
|
||||
|
||||
// Test GetNeighbors for vertex 3
|
||||
neighbors = g.GetNeighbors(3)
|
||||
expectedNeighbors = []Edge[int, int]{{ID: 2, src: 3, dest: 1, weight: 20}, {ID: 3, src: 3, dest: 2, weight: 30}}
|
||||
if !equalEdges(neighbors, expectedNeighbors) {
|
||||
t.Errorf("GetNeighbors() failed for vertex 3: expected %v, got %v", expectedNeighbors, neighbors)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if two slices of edges are equal
|
||||
func equalEdges(edges1, edges2 []Edge[int, int]) bool {
|
||||
if len(edges1) != len(edges2) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(edges1); i++ {
|
||||
if edges1[i] != edges2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestHasNeighbor(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add edges to the graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(1, 3, 20)
|
||||
g.AddEdge(2, 3, 30)
|
||||
|
||||
// Test HasNeighbor for existing neighbors
|
||||
if !g.HasNeighbor(1, 2) {
|
||||
t.Errorf("HasNeighbor() failed: expected true, got false")
|
||||
}
|
||||
if !g.HasNeighbor(1, 3) {
|
||||
t.Errorf("HasNeighbor() failed: expected true, got false")
|
||||
}
|
||||
if !g.HasNeighbor(2, 3) {
|
||||
t.Errorf("HasNeighbor() failed: expected true, got false")
|
||||
}
|
||||
if !g.HasNeighbor(3, 1) {
|
||||
t.Errorf("HasNeighbor() failed: expected true, got false")
|
||||
}
|
||||
if !g.HasNeighbor(2, 1) {
|
||||
t.Errorf("HasNeighbor() failed: expected true, got false")
|
||||
}
|
||||
|
||||
// Test HasNeighbor for non-existing neighbors
|
||||
if g.HasNeighbor(1, 4) {
|
||||
t.Errorf("HasNeighbor() failed: expected false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEdge(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 4, 30)
|
||||
|
||||
// Test existing edge
|
||||
expectedEdge := Edge[int, int]{ID: 1, src: 1, dest: 2, weight: 10}
|
||||
edge := g.GetEdge(1, 2)
|
||||
if edge != expectedEdge {
|
||||
t.Errorf("GetEdge() failed for existing edge: expected %v, got %v", expectedEdge, edge)
|
||||
}
|
||||
|
||||
// Test non-existing edge
|
||||
edge = g.GetEdge(1, 3)
|
||||
if edge != (Edge[int, int]{}) {
|
||||
t.Errorf("GetEdge() failed for non-existing edge: expected empty edge, got %v", edge)
|
||||
}
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add vertices and edges to the graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(1, 3, 20)
|
||||
g.AddEdge(2, 3, 30)
|
||||
|
||||
result := g.String()
|
||||
|
||||
if !strings.Contains(result, "1: (1, 1, 2, 10) (2, 1, 3, 20)\n") || !strings.Contains(result, "2: (1, 2, 1, 10) (3, 2, 3, 30)\n") || !strings.Contains(result, "3: (2, 3, 1, 20) (3, 3, 2, 30)\n") {
|
||||
t.Errorf("String() failed and got %q", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountComponents(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add vertices and edges to the graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(4, 5, 30)
|
||||
|
||||
expectedCount := 2
|
||||
actualCount := g.CountComponents()
|
||||
if actualCount != expectedCount {
|
||||
t.Errorf("CountComponents() failed: expected count = %d, got count = %d", expectedCount, actualCount)
|
||||
}
|
||||
|
||||
// Add another component to the graph
|
||||
g.AddEdge(6, 7, 40)
|
||||
g.AddEdge(7, 8, 50)
|
||||
|
||||
expectedCount = 3
|
||||
actualCount = g.CountComponents()
|
||||
if actualCount != expectedCount {
|
||||
t.Errorf("CountComponents() failed: expected count = %d, got count = %d", expectedCount, actualCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsConnected(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add vertices and edges to the graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 4, 30)
|
||||
|
||||
// Test IsConnected for a connected graph
|
||||
if !g.IsConnected() {
|
||||
t.Errorf("IsConnected() failed: expected true, got false")
|
||||
}
|
||||
|
||||
// Test IsConnected for a disconnected graph
|
||||
g.AddVertex(5)
|
||||
if g.IsConnected() {
|
||||
t.Errorf("IsConnected() failed: expected false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsCyclic(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add edges to create a cyclic graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 1, 30)
|
||||
|
||||
// Check if the graph is cyclic
|
||||
if !g.IsCyclic() {
|
||||
t.Errorf("IsCyclic() failed: expected true, got false")
|
||||
}
|
||||
|
||||
// Add edges to create an acyclic graph
|
||||
g = NewGraph[int, int]()
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 4, 30)
|
||||
|
||||
// Check if the graph is acyclic
|
||||
if g.IsCyclic() {
|
||||
t.Errorf("IsCyclic() failed: expected false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsCyclicUtil(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 1, 30)
|
||||
|
||||
visited := make(map[int]bool)
|
||||
recStack := make(map[int]bool)
|
||||
edgeIDs := make(map[int]bool)
|
||||
|
||||
// Test for cyclic graph
|
||||
if !g.isCyclicUtil(1, visited, recStack, edgeIDs) {
|
||||
t.Errorf("isCyclicUtil() failed: expected true, got false")
|
||||
}
|
||||
|
||||
g = NewGraph[int, int]()
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 4, 30)
|
||||
|
||||
visited = make(map[int]bool)
|
||||
recStack = make(map[int]bool)
|
||||
edgeIDs = make(map[int]bool)
|
||||
|
||||
// Test for acyclic graph
|
||||
if g.isCyclicUtil(1, visited, recStack, edgeIDs) {
|
||||
t.Errorf("isCyclicUtil() failed: expected false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsTree(t *testing.T) {
|
||||
// Create a new graph
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add vertices and edges to the graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 4, 30)
|
||||
|
||||
// Test IsTree for a connected acyclic graph
|
||||
if !g.IsTree() {
|
||||
t.Errorf("IsTree() failed for a connected acyclic graph")
|
||||
}
|
||||
|
||||
// Add an edge to create a cycle
|
||||
g.AddEdge(4, 1, 40)
|
||||
|
||||
// Test IsTree for a graph with a cycle
|
||||
if g.IsTree() {
|
||||
t.Errorf("IsTree() failed for a graph with a cycle")
|
||||
}
|
||||
|
||||
// Remove the edge to make the graph acyclic again
|
||||
g.RemoveEdge(4, 1)
|
||||
|
||||
// Remove a vertex to make the graph disconnected
|
||||
g.RemoveVertex(3)
|
||||
|
||||
// Test IsTree for a disconnected graph
|
||||
if g.IsTree() {
|
||||
t.Errorf("IsTree() failed for a disconnected graph")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsBipartite(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add vertices and edges to the graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(1, 3, 20)
|
||||
g.AddEdge(3, 4, 40)
|
||||
|
||||
// Test for a bipartite graph
|
||||
if !g.IsBipartite() {
|
||||
t.Error("IsBipartite() failed for a bipartite graph")
|
||||
}
|
||||
|
||||
// Add an edge to create a cycle
|
||||
g.AddEdge(4, 1, 50)
|
||||
|
||||
// Test for a non-bipartite graph with a cycle
|
||||
if g.IsBipartite() {
|
||||
t.Error("IsBipartite() failed for a non-bipartite graph with a cycle")
|
||||
}
|
||||
|
||||
// Remove the edge to break the cycle
|
||||
g.RemoveEdge(4, 1)
|
||||
|
||||
// Test for a non-bipartite graph without a cycle
|
||||
if !g.IsBipartite() {
|
||||
t.Error("IsBipartite() failed for a non-bipartite graph without a cycle")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsBipartiteUtil(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add vertices and edges to the graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 4, 30)
|
||||
|
||||
visited := make(map[int]bool)
|
||||
colors := make(map[int]bool)
|
||||
edgeIDs := map[int]bool{}
|
||||
|
||||
// Test isBipartiteUtil for a bipartite graph
|
||||
result := g.isBipartiteUtil(1, visited, colors, edgeIDs)
|
||||
expectedResult := true
|
||||
if result != expectedResult {
|
||||
t.Errorf("isBipartiteUtil() failed for bipartite graph: expected %v, got %v", expectedResult, result)
|
||||
}
|
||||
|
||||
visited = make(map[int]bool)
|
||||
colors = make(map[int]bool)
|
||||
edgeIDs = map[int]bool{}
|
||||
|
||||
// Test isBipartiteUtil for a non-bipartite graph
|
||||
g.AddEdge(4, 2, 40)
|
||||
result = g.isBipartiteUtil(1, visited, colors, edgeIDs)
|
||||
expectedResult = false
|
||||
if result != expectedResult {
|
||||
t.Errorf("isBipartiteUtil() failed for non-bipartite graph: expected %v, got %v", expectedResult, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddVertex(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add a vertex to the graph
|
||||
g.AddVertex(1)
|
||||
|
||||
// Check if the vertex is added correctly
|
||||
expectedAdjacencyList := map[int][]Edge[int, int]{
|
||||
1: {},
|
||||
}
|
||||
if !compareAdjacencyLists(g.adjacencyList, expectedAdjacencyList) {
|
||||
t.Errorf("AddVertex() failed: expected adjacency list = %v, got %v", expectedAdjacencyList, g.adjacencyList)
|
||||
}
|
||||
}
|
||||
func TestGetVertices(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add vertices to the graph
|
||||
g.AddVertex(1)
|
||||
g.AddVertex(2)
|
||||
g.AddVertex(3)
|
||||
|
||||
// Get the vertices from the graph
|
||||
vertices := g.GetVertices()
|
||||
|
||||
// Check if the vertices are returned correctly
|
||||
expectedVertices := []int{1, 2, 3}
|
||||
if !equalSlices(vertices, expectedVertices) {
|
||||
t.Errorf("GetVertices() failed: expected %v, got %v", expectedVertices, vertices)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if two slices are equal
|
||||
func equalSlices(a, b []int) bool {
|
||||
sort.Ints(a)
|
||||
sort.Ints(b)
|
||||
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestRemoveEdge(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 4, 30)
|
||||
|
||||
// Test removing an existing edge
|
||||
g.RemoveEdge(1, 2)
|
||||
if edges := g.GetEdges(); len(edges) != 2 {
|
||||
t.Errorf("RemoveEdge() failed: expected 2 edges, got %d", len(edges))
|
||||
}
|
||||
if neighbors := g.GetNeighbors(1); len(neighbors) != 0 {
|
||||
t.Errorf("RemoveEdge() failed: expected 0 neighbors for vertex 1, got %d", len(neighbors))
|
||||
}
|
||||
if neighbors := g.GetNeighbors(2); len(neighbors) != 1 {
|
||||
t.Errorf("RemoveEdge() failed: expected 1 neighbor for vertex 2, got %d", len(neighbors))
|
||||
}
|
||||
|
||||
// Test removing a non-existing edge
|
||||
g.RemoveEdge(1, 3)
|
||||
if edges := g.GetEdges(); len(edges) != 2 {
|
||||
t.Errorf("RemoveEdge() failed: expected 2 edges, got %d", len(edges))
|
||||
}
|
||||
if neighbors := g.GetNeighbors(1); len(neighbors) != 0 {
|
||||
t.Errorf("RemoveEdge() failed: expected 0 neighbors for vertex 1, got %d", len(neighbors))
|
||||
}
|
||||
if neighbors := g.GetNeighbors(3); len(neighbors) != 2 {
|
||||
t.Errorf("RemoveEdge() failed: expected 1 neighbor for vertex 3, got %d", len(neighbors))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveVertex(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add vertices and edges to the graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(1, 3, 20)
|
||||
g.AddEdge(2, 3, 30)
|
||||
g.AddEdge(3, 4, 40)
|
||||
|
||||
// Remove vertex 2
|
||||
g.RemoveVertex(2)
|
||||
|
||||
// Check if the vertex is removed correctly
|
||||
expectedAdjacencyList := map[int][]Edge[int, int]{
|
||||
1: {{2, 1, 3, 20}},
|
||||
3: {{2, 3, 1, 20}, {4, 3, 4, 40}},
|
||||
4: {{4, 4, 3, 40}},
|
||||
}
|
||||
if !compareAdjacencyLists(g.adjacencyList, expectedAdjacencyList) {
|
||||
t.Errorf("RemoveVertex() failed: expected adjacency list = %v, got %v", expectedAdjacencyList, g.adjacencyList)
|
||||
}
|
||||
|
||||
expectedEdges := []Edge[int, int]{
|
||||
{2, 1, 3, 20},
|
||||
{4, 3, 4, 40},
|
||||
}
|
||||
if !compareEdges(g.edges, expectedEdges) {
|
||||
t.Errorf("RemoveVertex() failed: expected edges list = %v, got %v", expectedEdges, g.edges)
|
||||
}
|
||||
|
||||
expectedV := 3
|
||||
if g.v != expectedV {
|
||||
t.Errorf("RemoveVertex() failed: expected v = %d, got v = %d", expectedV, g.v)
|
||||
}
|
||||
|
||||
expectedE := 2
|
||||
if g.e != expectedE {
|
||||
t.Errorf("RemoveVertex() failed: expected e = %d, got e = %d", expectedE, g.e)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEdges(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 4, 30)
|
||||
|
||||
// Test GetEdges
|
||||
expectedEdges := []Edge[int, int]{
|
||||
{ID: 1, src: 1, dest: 2, weight: 10},
|
||||
{ID: 2, src: 2, dest: 3, weight: 20},
|
||||
{ID: 3, src: 3, dest: 4, weight: 30},
|
||||
}
|
||||
edges := g.GetEdges()
|
||||
if !compareEdges(edges, expectedEdges) {
|
||||
t.Errorf("GetEdges() failed: expected edges = %v, got edges = %v", expectedEdges, edges)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveEdgeWithID(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 4, 30)
|
||||
|
||||
// Test removing an existing edge
|
||||
g.RemoveEdgeWithID(1)
|
||||
|
||||
// Check if the edge is removed from the list of edges
|
||||
expectedEdges := []Edge[int, int]{
|
||||
{ID: 2, src: 2, dest: 3, weight: 20},
|
||||
{ID: 3, src: 3, dest: 4, weight: 30},
|
||||
}
|
||||
if !compareEdges(g.edges, expectedEdges) {
|
||||
t.Errorf("RemoveEdgeWithID() failed: expected edges list = %v, got %v", expectedEdges, g.edges)
|
||||
}
|
||||
|
||||
// Check if the edge is removed from the adjacency list
|
||||
expectedAdjacencyList := map[int][]Edge[int, int]{
|
||||
1: {},
|
||||
2: {{2, 2, 3, 20}},
|
||||
3: {{2, 3, 2, 20}, {3, 3, 4, 30}},
|
||||
4: {{3, 4, 3, 30}},
|
||||
}
|
||||
if !compareAdjacencyLists(g.adjacencyList, expectedAdjacencyList) {
|
||||
t.Errorf("RemoveEdgeWithID() failed: expected adjacency list = %v, got %v", expectedAdjacencyList, g.adjacencyList)
|
||||
}
|
||||
|
||||
// Check if the edge count is updated
|
||||
expectedE := 2
|
||||
if g.e != expectedE {
|
||||
t.Errorf("RemoveEdgeWithID() failed: expected e = %d, got e = %d", expectedE, g.e)
|
||||
}
|
||||
|
||||
// Test removing a non-existing edge
|
||||
g.RemoveEdgeWithID(5)
|
||||
|
||||
// Check if the graph remains unchanged
|
||||
if !compareEdges(g.edges, expectedEdges) {
|
||||
t.Errorf("RemoveEdgeWithID() failed: expected edges list = %v, got %v", expectedEdges, g.edges)
|
||||
}
|
||||
if !compareAdjacencyLists(g.adjacencyList, expectedAdjacencyList) {
|
||||
t.Errorf("RemoveEdgeWithID() failed: expected adjacency list = %v, got %v", expectedAdjacencyList, g.adjacencyList)
|
||||
}
|
||||
if g.e != expectedE {
|
||||
t.Errorf("RemoveEdgeWithID() failed: expected e = %d, got e = %d", expectedE, g.e)
|
||||
}
|
||||
}
|
||||
func TestKargerMinCutUF(t *testing.T) {
|
||||
// Create a new graph
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add vertices and edges to the graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(1, 3, 20)
|
||||
g.AddEdge(2, 3, 30)
|
||||
g.AddEdge(3, 4, 40)
|
||||
g.AddEdge(4, 5, 50)
|
||||
g.AddEdge(4, 6, 60)
|
||||
g.AddEdge(5, 6, 70)
|
||||
|
||||
// Run Karger's algorithm to find the minimum cut
|
||||
cutEdges, resultSubsets := g.kargerMinCutUF(10000)
|
||||
|
||||
// Check if the number of cut edges is correct
|
||||
expectedCutEdges := 1
|
||||
if cutEdges != expectedCutEdges {
|
||||
t.Errorf("kargerMinCutUF() failed: expected cut edges = %d, got cut edges = %d", expectedCutEdges, cutEdges)
|
||||
}
|
||||
|
||||
// Check if the result subsets are correct
|
||||
expectedResultSubsets := [][]int{{1, 2, 3}, {4, 5, 6}}
|
||||
if !equalSubsets(resultSubsets, expectedResultSubsets) {
|
||||
t.Errorf("kargerMinCutUF() failed: expected result subsets = %v, got result subsets = %v", expectedResultSubsets, resultSubsets)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if two subsets are equal
|
||||
func equalSubsets(a, b [][]int) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(a); i++ {
|
||||
if !equalSlices(a[i], b[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func TestBFS(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
|
||||
// Add vertices and edges to the graph
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(1, 3, 20)
|
||||
g.AddEdge(2, 3, 30)
|
||||
g.AddEdge(2, 4, 40)
|
||||
g.AddEdge(3, 4, 50)
|
||||
|
||||
// Define the isGoal function
|
||||
isGoal := func(vertex int) bool {
|
||||
return vertex == 4
|
||||
}
|
||||
|
||||
// Perform BFS starting from vertex 1
|
||||
result := g.BFS(1, isGoal)
|
||||
|
||||
// Check if the result is correct
|
||||
expectedResult := []int{1, 2, 3, 4}
|
||||
if !equalSlices(result, expectedResult) {
|
||||
t.Errorf("BFS() failed: expected %v, got %v", expectedResult, result)
|
||||
}
|
||||
|
||||
// Perform BFS starting from vertex 2
|
||||
result = g.BFS(2, isGoal)
|
||||
|
||||
// Check if the result is correct
|
||||
expectedResult = []int{2, 1, 3, 4}
|
||||
if !equalSlices(result, expectedResult) {
|
||||
t.Errorf("BFS() failed: expected %v, got %v", expectedResult, result)
|
||||
}
|
||||
|
||||
// Perform BFS starting from vertex 3
|
||||
result = g.BFS(3, isGoal)
|
||||
|
||||
// Check if the result is correct
|
||||
expectedResult = []int{3, 1, 2, 4}
|
||||
if !equalSlices(result, expectedResult) {
|
||||
t.Errorf("BFS() failed: expected %v, got %v", expectedResult, result)
|
||||
}
|
||||
|
||||
// Perform BFS starting from vertex 4
|
||||
result = g.BFS(4, isGoal)
|
||||
|
||||
// Check if the result is correct
|
||||
expectedResult = []int{4}
|
||||
if !equalSlices(result, expectedResult) {
|
||||
t.Errorf("BFS() failed: expected %v, got %v", expectedResult, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDFS(t *testing.T) {
|
||||
g := NewGraph[int, int]()
|
||||
g.AddEdge(1, 2, 10)
|
||||
g.AddEdge(2, 3, 20)
|
||||
g.AddEdge(3, 4, 30)
|
||||
g.AddEdge(4, 5, 40)
|
||||
g.AddEdge(5, 6, 50)
|
||||
|
||||
// Test DFS with a goal function that returns true for vertex 6
|
||||
isGoal := func(vertex int) bool {
|
||||
return vertex == 6
|
||||
}
|
||||
|
||||
result := g.DFS(1, isGoal)
|
||||
expectedResult := []int{1, 2, 3, 4, 5, 6}
|
||||
if !equalSlices(result, expectedResult) {
|
||||
t.Errorf("DFS() failed with goal function: expected %v, got %v", expectedResult, result)
|
||||
}
|
||||
|
||||
// Test DFS with a goal function that returns true for vertex 3
|
||||
isGoal = func(vertex int) bool {
|
||||
return vertex == 3
|
||||
}
|
||||
|
||||
result = g.DFS(1, isGoal)
|
||||
expectedResult = []int{1, 2, 3}
|
||||
if !equalSlices(result, expectedResult) {
|
||||
t.Errorf("DFS() failed with goal function: expected %v, got %v", expectedResult, result)
|
||||
}
|
||||
|
||||
// Test DFS with a goal function that returns true for vertex 7 (non-existent vertex)
|
||||
isGoal = func(vertex int) bool {
|
||||
return vertex == 7
|
||||
}
|
||||
|
||||
result = g.DFS(1, isGoal)
|
||||
expectedResult = []int{}
|
||||
if !equalSlices(result, expectedResult) {
|
||||
t.Errorf("DFS() failed with goal function: expected %v, got %v", expectedResult, result)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue