Saturday, 25 November 2017

Generic graph implementation in C#

class Vertex<T>
{

    List<Vertex<T>> neighbors; 
    T value;
    bool isVisited;

    public List<Vertex<T>> Neighbors { get { return neighbors; } set { neighbors = value; } }
    public T Value { get { return value; } set { this.value = value; } }
    public bool IsVisited { get { return isVisited; } set { isVisited = value; } }
    public int NeighborsCount { get { return neighbors.Count; } }

    public Vertex(T value)
    {
        this.value = value;
        isVisited = false;
        neighbors = new List<Vertex<T>>();
    }

    public Vertex(T value, List<Vertex<T>> neighbors)
    {
        this.value = value;
        isVisited = false;
        this.neighbors = neighbors;
    }

    public void Visit()
    {
        isVisited = true;
    }

    public void AddEdge(Vertex<T> vertex)
    {
        neighbors.Add(vertex);
    }

    public void AddEdges(List<Vertex<T>> newNeighbors)
    {
        neighbors.AddRange(newNeighbors);
    }

    public void RemoveEdge(Vertex<T> vertex)
    {
        neighbors.Remove(vertex);
    }


    public override string ToString()
    {

        StringBuilder allNeighbors = new StringBuilder("");
        allNeighbors.Append(value + ": ");

        foreach (Vertex<T> neighbor in neighbors)
        {
            allNeighbors.Append(neighbor.value + "  ");
        }

        return allNeighbors.ToString();

    }

}

class UndirectedGenericGraph<T>
{

    // The list of vertices in the graph
    private List<Vertex<T>> vertices;

    // The number of vertices
    int size;

    public List<Vertex<T>> Vertices { get { return vertices; } }
    public int Size { get { return vertices.Count; } }


    public UndirectedGenericGraph(int initialSize)
    {
        if(size < 0)
        {
            throw new ArgumentException("Number of vertices cannot be negative");
        }

        size = initialSize;

        vertices = new List<Vertex<T>>(initialSize);

    }

    public UndirectedGenericGraph(List<Vertex<T>> initialNodes)
    {
        vertices = initialNodes;
        size = vertices.Count;
    }

    public void AddVertex(Vertex<T> vertex)
    {
        vertices.Add(vertex);
    }

    public void RemoveVertex(Vertex<T> vertex)
    {
        vertices.Remove(vertex);
    }

    public bool HasVertex(Vertex<T> vertex)
    {
        return vertices.Contains(vertex);
    }

    public void DepthFirstSearch(Vertex<T> root)
    {
        if (!root.IsVisited)
        {
            Console.Write(root.Value + " ");
            root.Visit();

            foreach(Vertex<T> neighbor in root.Neighbors)
            {
                DepthFirstSearch(neighbor);
            }

        }
    }


    public void BreadthFirstSearch(Vertex<T> root)
    {

        Queue<Vertex<T>> queue = new Queue<Vertex<T>>();

        root.Visit();

        queue.Enqueue(root);

        while(queue.Count > 0)
        {
            Vertex<T> current = queue.Dequeue();

            foreach (Vertex<T> neighbor in current.Neighbors)
            {
                if (!neighbor.IsVisited)
                {
                    Console.Write(neighbor.Value + " ");
                    neighbor.Visit();
                    queue.Enqueue(neighbor);
                }
            }
        }

    }

}

class Program
{
    static void Main(string[] args)
    {

        // Create a list of vertices using the Vertex<T> class
        List<Vertex<string>> vertices = new List<Vertex<string>>
        (
            new Vertex<string> []
                {
                new Vertex<string>("Los Angeles"),
                new Vertex<string>("San Francisco"),
                new Vertex<string>("Las Vegas"),
                new Vertex<string>("Seattle"),
                new Vertex<string>("Austin"),
                new Vertex<string>("Portland")
                }
        );

        // Establish edges; Ex. Los Angeles -> San Francisco, Las Vegas, Portland
        vertices[0].AddEdges(new List<Vertex<string>>(new Vertex<string>[]
        {
            vertices[1], vertices[2], vertices[5]
        }));

        vertices[1].AddEdges(new List<Vertex<string>>(new Vertex<string>[]
        {
            vertices[0], vertices[3], vertices[5]
        }));

        vertices[2].AddEdges(new List<Vertex<string>>(new Vertex<string>[]
        {
            vertices[0], vertices[1], vertices[4]
        }));

        vertices[3].AddEdges(new List<Vertex<string>>(new Vertex<string>[]
        {
            vertices[1], vertices[5]
        }));

        vertices[4].AddEdges(new List<Vertex<string>>(new Vertex<string>[]
        {
            vertices[2]
        }));

        vertices[5].AddEdges(new List<Vertex<string>>(new Vertex<string>[]
        {
            vertices[1], vertices[3]
        }));

        // Create graph using the UndirectedGenericGraph<T> class
        UndirectedGenericGraph<string> testGraph = new UndirectedGenericGraph<string>(vertices);

        // Check to see that all neighbors are properly set up
        foreach(Vertex<string> vertex in vertices)
        {
            Console.WriteLine(vertex.ToString());
        }

        // Test searching algorithms
        testGraph.DepthFirstSearch(vertices[0]);
        //testGraph.BreadthFirstSearch(vertices[0]);

    }
}

class Vertex<T>
{
    public Vertex(T value, params Vertex<T>[] parameters)
        : this(value, (IEnumerable<Vertex<T>>)parameters) { }

    public Vertex(T value, IEnumerable<Vertex<T>> neighbors = null)
    {
        Value = value;
        Neighbors = neighbors?.ToList() ?? new List<Vertex<T>>();
        IsVisited = false;  // can be omitted, default is false but some
                            // people like to have everything explicitly
                            // initialized
    }

    public T Value { get; }   // can be made writable

    public List<Vertex<T>> Neighbors { get; }

    public bool IsVisited { get; set; }

    public int NeighborCount  => Neighbors.Count;

    public void AddEdge(Vertex<T> vertex)
    {
        Neighbors.Add(vertex);
    }

    public void AddEdges(params Vertex<T>[] newNeighbors)
    {
        Neighbors.AddRange(newNeighbors);
    }

    public void AddEdges(IEnumerable<Vertex<T>> newNeighbors)
    {
        Neighbors.AddRange(newNeighbors);
    }

    public void RemoveEdge(Vertex<T> vertex)
    {
        Neighbors.Remove(vertex);
    }

    public override string ToString()
    {
        return Neighbors.Aggregate(new StringBuilder($"{Value}: "), (sb, n) => sb.Append($"{n.Value} ")).ToString();
        //return $"{Value}: {(string.Join(" ", Neighbors.Select(n => n.Value)))}";
    }

}

public void CheckVertices()
{
    var la = new Vertex<string>("Los Angeles");
    var sf = new Vertex<string>("San Francisco");
    var lv = new Vertex<string>("Las Vegas");
    var se = new Vertex<string>("Seattle");
    var au = new Vertex<string>("Austin");
    var po = new Vertex<string>("Portland");

    var testGraph = new UndirectedGenericGraph<string>();

    // la <=> sf, lv, po
    testGraph.AddPair(la, sf);
    testGraph.AddPair(la, lv);
    testGraph.AddPair(la, po);

    // sf <=> se, po
    testGraph.AddPair(sf, se);
    testGraph.AddPair(sf, po);

    // lv <=> au
    testGraph.AddPair(lv, au);

    // se <=> po
    testGraph.AddPair(se, po);

    // Check to see that all neighbors are properly set up
    foreach (var vertex in testGraph.Vertices)
    {
        System.Diagnostics.Debug.WriteLine(vertex.ToString());
     }

    // Test searching algorithms
    testGraph.DepthFirstSearch(la, (m) => System.Diagnostics.Debug.WriteLine(m));

}

class UndirectedGenericGraph<T>
{

    public UndirectedGenericGraph(params Vertex<T>[] initialNodes)
        : this((IEnumerable<Vertex<T>>)initialNodes) { }

    public UndirectedGenericGraph(IEnumerable<Vertex<T>> initialNodes = null)
    {
        Vertices = initialNodes?.ToList() ?? new List<Vertex<T>>();
    }

    public List<Vertex<T>> Vertices { get; }

    public int Size => Vertices.Count;

    public void AddPair(Vertex<T> first, Vertex<T> second)
    {
        AddToList(first);
        AddToList(second);
        AddNeighbors(first, second);
    }

    public void DepthFirstSearch(Vertex<T> root, Action<string> writer)
    {
        UnvisitAll();
        DepthFirstSearchImplementation(root, writer);

    }

    private void DepthFirstSearchImplementation(Vertex<T> root, Action<string> writer)
    {
        if (!root.IsVisited)
        {
            writer($"{root.Value} ");
            root.IsVisited = true;

            foreach (Vertex<T> neighbor in root.Neighbors)
            {
                DepthFirstSearchImplementation(neighbor, writer);
            }
        }
    }

    private void AddToList(Vertex<T> vertex)
    {
        if (!Vertices.Contains(vertex))
        {
            Vertices.Add(vertex);
        }
    }

    private void AddNeighbors(Vertex<T> first, Vertex<T>  second)
    {
        AddNeighbor(first, second);
        AddNeighbor(second, first);
    }

    private void AddNeighbor(Vertex<T> first, Vertex<T> second)
    {
        if (!first.Neighbors.Contains(second))
        {
            first.AddEdge(second);
        }
    }

    private void UnvisitAll()
    {
        foreach(var vertex in Vertices)
        {
            vertex.IsVisited = false;
        }
    }

}

class DeepthFirstIterator<T>
{
    private readonly Vertex<T> root;
    private readonly HashSet<Vertex<T>> visited = new HashSet<Vertex<T>>();
    public DeepthFirstIterator(Vertex<T> rootVertex)
    {
        root = rootVertex;
    }

    public IEnumerable<Vertex<T>> Iterate()
    {
        visited.Clear();

        return DepthFirstSearch(root, visited);
    }

    private IEnumerable<Vertex<T>> DepthFirstSearch(Vertex<T> vertex, HashSet<Vertex<T>> visited)
    {
        if (visited.Contains(vertex))
            yield break;

        visited.Add(vertex);

        yield return vertex;

        foreach (Vertex<T> neighbor in vertex.Neighbors.SelectMany(n => DepthFirstSearch(n, visited)))
            yield return neighbor;
    }
}

No comments:

Post a Comment