Theodor Zoulias
Theodor Zoulias

Reputation: 43553

How to get the vertices of an abstract polygon with known edges

This is a restatement of a question that has now been deleted. I think it is an interesting question. The input of the problem is an array of two-element tuples, each one representing an abstract edge that connects two abstract vertices. The desired output is an array of connected vertices. A vertex can be of any type, not necessarily a point in space, hence the "abstract" designation. The array is not expected to be ordered in any way. Actually the types are not even comparable. We are only allowed to compare them for equality.

Samples of input and output:

var input = new[] { ('a', 'b'), ('c', 'b'), ('a', 'c') };
var output = new[] { 'a', 'b', 'c' };

var input = new[] { ("a", "b"), ("a", "b") };
var output = new[] { "a", "b" };

var input = new[] { (true, true) };
var output = new[] { true };

var input = new[] { (1, 2), (4, 3), (3, 2), (1, 4) };
var output = new[] { 1, 2, 3, 4 };

var input = new[] { (1, 2), (2, 3), (3, 4) };
var output = new InvalidDataException(
    "Vertices [1, 4] are not connected with exactly 2 other vertices.");

var input = new[] { (1, 2), (2, 1), (3, 4), (4, 3) };
var output = new InvalidDataException(
    "Vertices [3, 4] are not connected with the rest of the graph.");

Method signature:

public static T[] GetVerticesFromEdges<T>((T, T)[] edges,
    IEqualityComparer<T> comparer);

Upvotes: 0

Views: 387

Answers (3)

DotNet Developer
DotNet Developer

Reputation: 3018

class EqualityComparerExtensions that will return a value indicating if two edges are neighbors.

static class EqualityComparerExtensions
{
    internal static bool Neighbours<T>(this IEqualityComparer<T> comparer, 
        Tuple<T, T> a, Tuple<T, T> b)
    {
        return comparer.Equals(a.Item1, b.Item1) 
            || comparer.Equals(a.Item1, b.Item2)
            || comparer.Equals(a.Item2, b.Item1) 
            || comparer.Equals(a.Item2, b.Item2);
    }
}

Then the algorithm would be:

public static T[] GetVerticesFromEdges<T>(Tuple<T, T>[] edges, 
    IEqualityComparer<T> comparer)
{
    var array = edges.Clone() as Tuple<T, T>[];
    var last = array.Length - 1;
    for (int i = 0; i < last; i++)
    {
        var c = 0;
        for (int j = i + 1; j < array.Length; j++)
        {
            if (comparer.Neighbours(array[i], array[j]))
            {
                var t = array[i + 1];
                array[i + 1] = array[j];
                array[j] = t;
                c++;
            }
        }
        if (c > 2 || c == 0)
        {
            throw new ArgumentException($"{nameof(edges)} is not a Polygon!");
        }
    }
    if (!comparer.Neighbours(array[last], array[0]))
    {
        throw new ArgumentException($"{nameof(edges)} is not a Polygon!");
    }
    for (int i = 0, j = 1; j < array.Length; i++, j++)
    {
        if (!comparer.Equals(array[i].Item2, array[j].Item1))
        {
            if (comparer.Equals(array[i].Item2, array[j].Item2))
            {
                array[j] = Tuple.Create(array[j].Item2, array[j].Item1);
            }
            else
            {
                array[i] = Tuple.Create(array[i].Item2, array[i].Item1);
            }
        }
    }
    if (!comparer.Equals(array[last].Item2, array[0].Item1))
    {
        throw new ArgumentException($"{nameof(edges)} is not a Polygon!");
    }
    return array.Select(a => a.Item1).ToArray();
}

Upvotes: 1

Theodor Zoulias
Theodor Zoulias

Reputation: 43553

Here is my solution.

public static T[] GetVerticesFromEdges<T>((T, T)[] edges,
    IEqualityComparer<T> comparer)
{
    if (edges.Length == 0) return new T[0];
    var vertices = new Dictionary<T, List<T>>(comparer);

    void AddVertex(T vertex, T connectedVertex)
    {
        if (!vertices.TryGetValue(vertex, out var connectedVertices))
        {
            connectedVertices = new List<T>();
            vertices[vertex] = connectedVertices;
        }
        connectedVertices.Add(connectedVertex);
    }

    foreach (var edge in edges)
    {
        AddVertex(edge.Item1, edge.Item2);
        AddVertex(edge.Item2, edge.Item1);
    }
    var invalid = vertices.Where(e => e.Value.Count != 2).Select(e => e.Key);
    if (invalid.Any())
    {
        throw new InvalidDataException(
            "Vertices [" + String.Join(", ", invalid) +
            "] are not connected with exactly 2 other vertices.");
    }

    var output = new List<T>();
    var currentVertex = vertices.Keys.First();
    while (true)
    {
        output.Add(currentVertex);
        var connectedVertices = vertices[currentVertex];
        vertices.Remove(currentVertex);
        if (vertices.ContainsKey(connectedVertices[0]))
        {
            currentVertex = connectedVertices[0];
        }
        else if (vertices.ContainsKey(connectedVertices[1]))
        {
            currentVertex = connectedVertices[1];
        }
        else
        {
            break;
        }
    }
    if (vertices.Count > 0)
    {
        throw new InvalidDataException(
            "Vertices [" + String.Join(", ", vertices.Keys) +
            "] are not connected with the rest of the graph.");
    }
    return output.ToArray();
}

Upvotes: 0

Aligus
Aligus

Reputation: 1605

You trying to find connected components of an undirected graph. Strictly speaking, not connected components in general, but one component special case.

You can find more about them in Wikipedia page and/or take a look at an example implementation in C#

Upvotes: 0

Related Questions