使用Linq构建图类;你能让这段代码看起来更好吗?

时间:2010-11-28 04:43:19

标签: c# linq graph

我正在创建一个不可变的简单图类(它不需要支持循环或多个边)。每个节点由一个整数值表示,从0到numNodes。

此代码有效,但我认为Linq查询和for循环相当丑陋。你能想出一个更清晰的方法来填充_edges吗?

public class Graph {
    private IList<IList<int>> _edges;
    public int Nodes { get; private set; 
}

public Graph(int numNodes, IList<Tuple<int,int>> edges) {
    Nodes = numNodes;
    _edges = new List<IList<int>>();

    for(int i = 0; i < numNodes; i++) {
        _edges.Add(new List<int>(
        (from e in edges where e.Item1 == i 
        select e.Item2).Union(
        (from e in edges where e.Item2 == i
        select e.Item1).Distinct())));
    }
}

public IEnumerable<int> Neighbors(int node) {
    return _edges[node];
}
}

3 个答案:

答案 0 :(得分:2)

这样的事情:

public class Graph2
{
    private IList<HashSet<int>> _edges;

    public int Nodes { get; private set; }

    public Graph2(int numNodes, IList<Tuple<int, int>> edges)
    {
        Nodes = numNodes;
        _edges = new List<HashSet<int>>(numNodes);
        for (int i = 0; i < numNodes; i++)
        {
            _edges.Add(new HashSet<int>());
        }

        foreach (var edge in edges)
        {
            _edges[edge.Item1].Add(edge.Item2);
            _edges[edge.Item2].Add(edge.Item1);
        }
    }

    public IEnumerable<int> Neighbors(int node)
    {
        return _edges[node];
    }
}

使用HashSet意味着您不必担心重复或具有复杂的Linq表达式。你也只能循环一次。

不幸的是,你仍然有初始的for循环来创建空的HashSets。

答案 1 :(得分:1)

public Graph(int numNodes, IList<Tuple<int, int>> edges)
{
    Nodes = numNodes;

    _edges = Enumerable.Range(0, numNodes).Select(num =>
                    edges.Where(x => x.Item1 == num).Select(x => x.Item2)
                            .Union(edges.Where(x => x.Item2 == num).Select(x => x.Item1))
                            .Distinct().ToList() as IList<int>
        ).ToList();
}

答案 2 :(得分:1)

以下是使用group by的替代方法:

public Graph(int numNodes, IList<Tuple<int, int>> edges) {
    Nodes = numNodes;
    _edges = new IList<int>[numNodes];
    var groups = from e in edges 
        group e by e.Item1 into g
        select new { Key = g.Key, Items = g.Select(s => s.Item2) };

    foreach (var grp in groups) {
        _edges[grp.Key] = grp.Items.ToList();
    }
}

这将避免初始化边集合的初始for循环,但是当然在访问邻居时必须小心。

或许更漂亮的是:

public class Graph {
    private IList<IEnumerable<int>> _edges;
    public int Nodes { get; private set; }    
    public Graph(int numNodes, IList<Tuple<int, int>> edges) {
        Nodes = numNodes;
        _edges = new IList<int>[numNodes];
        var groups = from e in edges
            group e by e.Item1 into g select g;

        foreach (var grp in groups) {
            _edges[grp.Key] = (from g in grp select g.Item2).ToList();
        }
    }

    public IEnumerable<int> Neighbors(int node) {
        return _edges[node];
    }
}