如何获得具有已知边的抽象多边形的顶点

时间:2019-07-16 09:53:54

标签: c# .net algorithm graph

这是a question的重述,现已被删除。我认为这是一个有趣的问题。问题的输入是一个由两个元素组成的元组的数组,每个元组代表一个连接两个抽象顶点的抽象边。所需的输出是一组连接的顶点。顶点可以是任何类型,不一定是空间点,因此是“抽象”名称。不应以任何方式订购该阵列。实际上,这些类型甚至都不具有可比性。我们只允许比较它们的相等性。

输入和输出示例:

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.");

方法签名:

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

3 个答案:

答案 0 :(得分:1)

EqualityComparerExtensions将返回一个值,该值指示两个边是否是相邻边。

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);
    }
}

那么算法将是:

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();
}

答案 1 :(得分:0)

您试图查找无向图的连接组件。严格来说,不是一般连接的组件,而是一种特殊情况的组件。

您可以在Wikipedia page中找到有关它们的更多信息,和/或查看示例implementation in C#

答案 2 :(得分:0)

这是我的解决方法。

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();
}