【问题标题】:Permutations of string collections in C#C#中字符串集合的排列
【发布时间】:2023-03-14 06:58:01
【问题描述】:

好像我又一次被递归算法困住了……

我的应用程序应该根据用户指定的信息和由如下字符串表示的子文件夹结构将文件排序到不同的文件夹:

[ROOT] \ brand \ color \ material \ 

结构字符串中的标签代表集合:

假设:

var brand = new List<string> { "Nike", "Adidas", "Reebok" };
var color = new List<string> { "red", "blue", "yellow", "black" };
var material = new List<string> { "leather", "fabric" };

var data = new List<List<string>>() { brand, color, material };

而我想要得到的是这样的:

[ROOT]\Nike\red\leather
[ROOT]\Nike\red\fabric
[ROOT]\Nike\blue\leather
[ROOT]\Nike\blue\fabric
[ROOT]\Nike\yellow\leather
[ROOT]\Nike\yellow\fabric
[ROOT]\Nike\black\leather
[ROOT]\Nike\black\fabric
[ROOT]\Adidas\red\leather
[ROOT]\Adidas\red\fabric
[ROOT]\Adidas\blue\leather
[ROOT]\Adidas\blue\fabric
[ROOT]\Adidas\yellow\leather
[ROOT]\Adidas\yellow\fabric
[ROOT]\Adidas\black\leather
[ROOT]\Adidas\black\fabric
[ROOT]\Reebok\red\leather
[ROOT]\Reebok\red\fabric
[ROOT]\Reebok\blue\leather
[ROOT]\Reebok\blue\fabric
[ROOT]\Reebok\yellow\leather
[ROOT]\Reebok\yellow\fabric
[ROOT]\Reebok\black\leather
[ROOT]\Reebok\black\fabric

问题是数据标签(品牌、颜色、材质)的数量和它们的顺序是事先不知道的,因此需要递归。

有什么想法吗?

非常感谢您!

【问题讨论】:

  • 当您收到一个文件时,您知道它的属性对吗?您不能使用这些属性即时创建文件夹吗?递归在哪里发挥作用?
  • var List data ... 部分无法编译。我不明白你为什么需要这个。如果您的文件夹结构级别是固定的,您只需要遍历每个文件,然后确定将其放入哪个(子)文件夹。
  • 用户在程序界面上放置一个文件,然后他必须通过从 UI 中的菜单中选择来设置相关的元数据。对于一个文件,甚至可以有 10 个不同的元数据字段左右:所有详细数据都存储在数据库中,而最重要的标签是根据结构字符串创建文件夹结构。该字符串在配置文件中设置,用户可以在启动程序之前选择其级别。他可以决定要使用多少元数据字段来创建字符串及其顺序。因此我事先不知道我需要多少次迭代。
  • 请注意:这里要求的是技术上的组合,而不是排列。排列涉及排序,因此 Nike\red\leatherleather\Nike\red 将是两个不同的有效排列。

标签: c# arrays string algorithm recursion


【解决方案1】:

这是由 Eric Lippert 编写的代码。笛卡尔积..

http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/

public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
    // base case: 
    IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() };
    foreach (var sequence in sequences)
    {
        var s = sequence; // don't close over the loop variable 
        // recursive case: use SelectMany to build the new product out of the old one 
        result =
            from seq in result
            from item in s
            select seq.Concat(new[] { item });
    }
    return result;
}

var result = CartesianProduct(new List<List<string>>() {brand,color,material });

用法示例:

var brand = new List<string> { "Nike", "Adidas", "Reebok" };
var color = new List<string> { "red", "blue", "yellow", "black" };
var material = new List<string> { "leather", "fabric" };

foreach (var row in CartesianProduct(new List<List<string>>() { brand, color, material }))
{
    Console.WriteLine(String.Join(",", row));
}

【讨论】:

  • 像魅力一样工作,谢谢!我只需要将 CartesianProduct 的参数更改为变量(我的列表具有任意长度),它完全符合我的要求。也谢谢你的参考!
  • @vi3x:我对您所说的“我将CartesianProduct 的参数更改为变量”的意思很感兴趣。它已经接受了任意数量的任意长度的列表...
  • @Chris,也许我不太熟悉英语(或编程!),但我的函数看起来像“foreach(CartesianProduct(ListOfLists)中的var row”,其中“ListOfLists”填充在运行时。我事先不知道它会包含多少子列表和哪些子列表。
【解决方案2】:

这是将当前结果与下一个结果的每个成员交叉连接的简单方法。 为此,您还需要创建任何包含 [ROOT] 的数组。

        var root = new List<string> { "[ROOT]" };
        var brand = new List<string> { "Nike", "Adidas", "Reebok" };
        var color = new List<string> { "red", "blue", "yellow", "black" };
        var material = new List<string> { "leather", "fabric" };

        var data = new List<List<string>>() { root, brand, color, material };

        IEnumerable<string> lstComb = new List<string> { null };
        foreach (var list in data)
        {
            lstComb = lstComb.SelectMany(o => list.Select(s => $"{o}\\{s}".TrimStart(new char[]{'\\'})));
        }

注意:将项目添加到包含列表的 data 列表时必须保持顺序。

【讨论】:

    猜你喜欢
    • 2019-09-09
    • 2010-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-18
    • 1970-01-01
    相关资源
    最近更新 更多