Обойти дерево в глубину

205
03 августа 2018, 13:50

Есть такой набор данных:

var replies1 = new List<int>() { 1, 4, 7 };
var replies2 = new List<int>() { 2, 5, 8 };
var replies3 = new List<int>() { 3, 6, 9 };

Есть массив массивов, эти массивы агрегирующий:

var mas =  new List<List<int>>() { replies1, replies2, replies3 };

Необходимо построить все возможные маршруты. Маршруты будут такими:

123
423
723
156
456
756
...

Количество массивов может быть неограниченным, как и их длина. По сути это дерево со множеством вершин. Подскажите пожалуйста, как можно реализовать алгоритм, который соберет все возможные маршруты? Очевидно, что реализовать можно рекурсивно, но мои тщетные попытки ни к чему не привели.

Решение с фиксированным количеством массивов:

 for (int i = 0; i < replies1.Count; i++)
            {
                for (int j = 0; j < replies2.Count; j++)
                {
                    for (int k = 0; k < replies3.Count; k++)
                    {
                        Console.Write(String.Format("{0}, {1}, {2}", replies1[i], replies2[j], replies3[k]));
                        Console.WriteLine();
                    }
                }
            }
Answer 1

Как правильно написали в комментариях, вам нужно декартово произведение множеств (или вы неправильно описали задачу и привели неправильный пример для фиксированного числа множеств).

Для заранее неизвестного числа множеств можно написать такой рекурсивный вариант:

static class Extensions
{
    public static IEnumerable<T> Cartesian<T>(this IEnumerable<IEnumerable<T>> source, Func<T, T, T> aggregator)
    {
        var list = source as List<IEnumerable<T>> ?? source.ToList();
        return CartesianImpl(list, list.Count - 1, aggregator);
    }
    static IEnumerable<T> CartesianImpl<T>(List<IEnumerable<T>> list, int startIndex, Func<T, T, T> aggregator)
    {
        if (startIndex > 0)
        {
            foreach (var y in list[startIndex])
                foreach (var x in CartesianImpl(list, startIndex - 1, aggregator))
                    yield return aggregator(x, y);
        }
        else
        {
            foreach (var e in list[startIndex])
                yield return e;
        }
    }
}

Более простой вариант, с использованием итератора, но возвращающий последовательности немного в другом порядке:

static class Extensions
{
    public static IEnumerable<T> Cartesian<T>(this IEnumerable<IEnumerable<T>> source, Func<T, T, T> aggregator)
    {
        var enumerator = source.GetEnumerator();
        if (!enumerator.MoveNext()) return Enumerable.Empty<T>();
        var result = enumerator.Current;
        while (enumerator.MoveNext())
        {
            var current = enumerator.Current;
            result = result.SelectMany(e => current, (x, y) => aggregator(x, y));
        }
        return result;
    }
}

Используем:

var replies1 = new List<int>() { 1, 4, 7 };
var replies2 = new List<int>() { 2, 5, 8 };
var replies3 = new List<int>() { 3, 6, 9 };
var mas = new List<List<int>>() { replies1, replies2, replies3 };
var res = mas.Cartesian((x, y) => 10 * x + y);
Console.WriteLine(string.Join("\n", res));
READ ALSO
Excel выбор листа при импорте laravel

Excel выбор листа при импорте laravel

Как сделал выборку листов при импорте из Excel файла в бд laravelНа данный момент мой импорт:

195
Doctrine не подтягивать все связи

Doctrine не подтягивать все связи

Например, когда я пишу так: $users = $this->getDoctrine()->getRepository('ProfileBundle:User')->findBy(['deletedAt' => null]); то к модели пользователя подтягиваются все связанные...

151
Генерация картинки с помощью php [дубликат]

Генерация картинки с помощью php [дубликат]

На данный вопрос уже ответили:

190
Warning: mail(): Multiple or malformed newlines found in additional_header

Warning: mail(): Multiple or malformed newlines found in additional_header

Помогите пофиксить ошибку, вот код отправки письма

207