在C#中合并相似的字符

8

我有一个包含整数的列表嵌套列表(这个列表可以是任意长度,可以包含任意数量的整数):

{{1,2}, {3,4}, {2,4}, {9,10}, {9,12,13,14}}

我想要做的下一步是将任何一个列表中的整数与其他任何列表中的整数匹配,例如:
   result = {{1,2,3,4}, {9,10,12,13,14}}

我尝试了许多不同的方法,但却无法找到一个优美的解决方案。


2
这里的“第一或第二”有什么重要性?特别是一个微小的边缘情况是,如果我们先合并:{3,4}{2,4}以生成{3,4,2}(基于4),那么它将无法与{1,2}匹配...?还是顺序是基于数字而不是位置的?或者只是“在它们相交时合并”? - Marc Gravell
“squares”与2、3或更多整数的序列之间的关系不清楚。 - Jirka Hanika
1
我猜两个foreach循环就可以了,但为了看到更加优雅的解决方案,我们需要看到你刚刚结束的解决方案。 - nawfal
5个回答

5
如果您只是想“在交叉点处合并”,那么可以使用以下代码,输出结果如下:
{1,2,3,4}
{9,10,12}

注意:它还通过了您的编辑测试,输出结果为:
{1,2,3,4}
{9,10,12,13,14}

代码:

static class Program {
    static void Main()
    {
        var sets = new SetCombiner<int> {
            {1,2},{3,4},{2,4},{9,10},{9,12}
        };
        sets.Combine();
        foreach (var set in sets)
        {
            // edited for unity: original implementation
            // Console.WriteLine("{" +
            //    string.Join(",", set.OrderBy(x => x)) + "}");

            StringBuilder sb = new StringBuilder();
            foreach(int i in set.OrderBy(x => x)) {
                if(sb.Length != 0) sb.Append(',');
                sb.Append(i);
            }
            Console.WriteLine("{" + sb + "}");
        }
    }
}

class SetCombiner<T> : List<HashSet<T>>
{
    public void Add(params T[] values)
    {
        Add(new HashSet<T>(values));
    }
    public void Combine()
    {
        int priorCount;
        do
        {
            priorCount = this.Count;
            for (int i = Count - 1; i >= 0; i--)
            {
                if (i >= Count) continue; // watch we haven't removed
                int formed = i;
                for (int j = formed - 1; j >= 0; j--)
                {
                    if (this[formed].Any(this[j].Contains))
                    { // an intersection exists; merge and remove
                        this[j].UnionWith(this[formed]);
                        this.RemoveAt(formed);
                        formed = j;
                    }
                }
            }
        } while (priorCount != this.Count); // making progress
    }
}

谢谢,这个很好用。(我使用在线编译器测试过了)。我正在使用Unity3d进行编程,它抛出了这个错误:error CS1503: Argument #2' cannot convert System.Linq.IOrderedEnumerable<int>' expression to type `string[]'。不过我认为这可能是Unity3D特有的问题。 - Ian Clay
谢谢,现在它完美地运行了 :) 我 '几乎' 朝着正确的方向前进了,使用t.Any(b.Contains),但这对我的学习帮助很大(我无法弄清楚如何将其变成类或如何构建for循环结构)。非常感激。 - Ian Clay
我注意到我的问题与我的初始问题标题不同:“在C#中合并相似的字符”。有没有关于新标题的建议以及如何编辑标题的方法? - Ian Clay
Unity3D使用C#的一个子集,加上一些自己的添加,因此在仅使用纯C#时正常工作的某些内容不受Unity支持。 Linq就是其中之一。我有一些可以正常工作的,但肯定还有一些Linq表达式是Unity不喜欢的。 - Darrel Hoffman
主要是核心库不同,对吧?(不是语言) - Marc Gravell
@MarcGravell 还有一些其他的事情有时候也会让我感到烦恼。我遇到的最大问题是在哪里可以使用 static 关键字以及在哪里不能使用的差异。Unity 会对在普通的 C# 中完全正常运行的代码产生错误 - 而这个问题与库无关。 - Darrel Hoffman

1

构建自定义比较器:

public class CusComparer : IComparer<int[]>
{
    public int Compare(int[] x, int[] y)
    {
        x = x.OrderBy(i => i).ToArray();
        y = y.OrderBy(i => i).ToArray();

        for (int i = 0; i < Math.Min(x.Length, y.Length); i++ )
        {
            if (x[i] < y[i]) return -1;
            if (x[i] > y[i]) return 1;
        }

         if (x.Length < y.Length) return -1;
        if (x.Length > y.Length) return 1;

        return 0;
    }
}

然后,首先按自定义比较器排序:

List<int[]> input = new List<int[]>()
        {
            new[] { 3, 4 }, new[] { 1, 2 }, new[] { 2, 4 }, 
            new[] { 9, 10 }, new[] { 9, 12 }
        };

var orderedInput = input.OrderBy(x => x, new CusComparer()).ToList();

使用 Intersect.Any() 进行检查:

List<int[]> output = new List<int[]>();

int[] temp = orderedInput[0];

foreach (var arr in orderedInput.Skip(1))
{
    if (temp.Intersect(arr).Any())
        temp = temp.Union(arr).ToArray();

    else
    {
        output.Add(temp);
        temp = arr;
    }
}

output.Add(temp);

0
这是一个使用LINQ的Aggregate的简单而灵活的解决方案:
void Main()
{
    var ints = new []{new []{1,2},new []{3,4},new []{2,4},new []{9,10},new []{9,12}};
    var grouped = ints.Aggregate(new List<HashSet<int>>(), Step);

    foreach(var bucket in grouped)
        Console.WriteLine(String.Join(",", bucket.OrderBy(b => b)));
}

static List<HashSet<T>> Step<T>(List<HashSet<T>> all, IEnumerable<T> current)
{
    var bucket = new HashSet<T>();

    foreach (var c in current)
        bucket.Add(c);

    foreach (var i in all.Where(b => b.Overlaps(bucket)).ToArray())
    {
        bucket.UnionWith(i);
        all.Remove(i);
    }
    all.Add(bucket);

    return all; 
}

0

我们维护一个结果集列表(1)。对于每个源集,删除与其相交的结果集(2),并添加一个新的结果集(3),该结果集是已删除的集合和源集的并集(4):

class Program {

    static IEnumerable<IEnumerable<T>> CombineSets<T>(
        IEnumerable<IEnumerable<T>> sets,
        IEqualityComparer<T> eq
    ) {

        var result_sets = new LinkedList<HashSet<T>>();         // 1

        foreach (var set in sets) {
            var result_set = new HashSet<T>(eq);                // 3
            foreach (var element in set) {
                result_set.Add(element);                        // 4
                var node = result_sets.First;
                while (node != null) {
                    var next = node.Next;
                    if (node.Value.Contains(element)) {         // 2
                        result_set.UnionWith(node.Value);       // 4
                        result_sets.Remove(node);               // 2
                    }
                    node = next;
                }
            }
            result_sets.AddLast(result_set);                    // 3
        }

        return result_sets;

    }

    static IEnumerable<IEnumerable<T>> CombineSets<T>(
        IEnumerable<IEnumerable<T>> src
    ) {
        return CombineSets(src, EqualityComparer<T>.Default);
    }

    static void Main(string[] args) {

        var sets = new[] {
            new[] { 1, 2 }, 
            new[] { 3, 4 }, 
            new[] { 2, 4 }, 
            new[] { 9, 10 }, 
            new[] { 9, 12, 13, 14 }
        };

        foreach (var result in CombineSets(sets))
            Console.WriteLine(
                "{{{0}}}",
                string.Join(",", result.OrderBy(x => x))
            );

    }

}

这将打印:

{1,2,3,4}
{9,10,12,13,14}

-1

好的,我使用LINQ连接了它!希望这是你想要的...有点疯狂;)

void Main()
{
    var matches = new List<List<ComparissonItem>> { /*Your Items*/ };

    var overall =
        from match in matches
        let matchesOne =
            (from searchItem in matches
             where searchItem.Any(item => match.Any(val => val.Matches(item) && !val.Equals(item)))
             select searchItem)
        where matchesOne.Any()
        select
            matchesOne.Union(new List<List<ComparissonItem>> { match })
            .SelectMany(item => item);

    var result = overall.Select(item => item.ToHashSet());
}

static class Extensions
{

    public static HashSet<T> ToHashSet<T>(this IEnumerable<T> enumerable)
    {
        return new HashSet<T>(enumerable);
    }
}

class ComparissonItem
{
    public int Value { get; set; }

    public bool Matches(ComparissonItem item)
    {
        /* Your matching logic*/
    }

    public override bool Equals(object obj)
    {
        var other = obj as ComparissonItem;
        return other == null ? false : this.Value == other.Value;
    }

    public override int GetHashCode()
    {
        return this.Value.GetHashCode();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接