Ваша проблема перестановок - это в основном просто проблема перестановки индексов.Если вы можете упорядочить числа от 0 до n - 1 во всех возможных вариантах, вы можете использовать их в качестве индексов вашего входного массива и просто скопировать строки.Следующий алгоритм не является оптимальным, но он достаточно графический, чтобы объяснять и реализовывать итеративно.
public static String[][] getAllPermutations(String[] str) {
LinkedList<Integer> current = new LinkedList<>();
LinkedList<Integer[]> permutations = new LinkedList<>();
int length = str.length;
current.add(-1);
while (!current.isEmpty()) {
// increment from the last position.
int position = Integer.MAX_VALUE;
position = getNextUnused(current, current.pop() + 1);
while (position >= length && !current.isEmpty()) {
position = getNextUnused(current, current.pop() + 1);
}
if (position < length) {
current.push(position);
} else {
break;
}
// fill with all available indexes.
while (current.size() < length) {
// find first unused index.
int unused = getNextUnused(current, 0);
current.push(unused);
}
// record result row.
permutations.add(current.toArray(new Integer[0]));
}
// select the right String, based on the index-permutation done before.
int numPermutations = permutations.size();
String[][] result = new String[numPermutations][length];
for (int i = 0; i < numPermutations; ++i) {
Integer[] indexes = permutations.get(i);
String[] row = new String[length];
for (int d = 0; d < length; ++d) {
row[d] = str[indexes[d]];
}
result[i] = row;
}
return result;
}
public static int getNextUnused(LinkedList<Integer> used, Integer current) {
int unused = current != null ? current : 0;
while (used.contains(unused)) {
++unused;
}
return unused;
}
Метод getAllPermutations организован в части инициализации, цикле, собирающем все перестановки (числовые), и, наконец,преобразование найденной перестановки индекса в перестановки строк.
Поскольку преобразование из int в строку является тривиальным, я просто объясню часть коллекции.Цикл повторяется до тех пор, пока представление не полностью исчерпано или завершено изнутри.
Сначала мы увеличиваем представление (current
).Для этого мы берем последнюю цифру и увеличиваем ее до следующего свободного значения.Затем мы выскакиваем, если мы выше длины, и смотрим на следующую цифру (и увеличиваем ее).Мы продолжаем это, пока не достигнем допустимого значения (ниже длины).
После этого мы заполняем оставшуюся часть цифр всеми оставшимися цифрами.После этого мы сохраняем текущее представление в списке массивов.
Этот алгоритм не является оптимальным с точки зрения времени выполнения!Куча быстрее.Но реализация Heap итеративно требует нетривиального стека, который утомителен для реализации / объяснения.