Да, это алгоритм «следующая перестановка», и это довольно просто слишком. В стандартной библиотеке шаблонов C ++ (STL) даже есть вызываемая функция next_permutation
.
Алгоритм фактически находит следующую перестановку - лексикографически следующую. Идея такова: предположим, вам дана последовательность, скажем «32541». Какая следующая перестановка?
Если вы подумаете, то увидите, что это «34125». И ваши мысли, вероятно, были примерно такими: В "32541",
- невозможно сохранить фиксированное число «32» и найти более позднюю перестановку в части «541», потому что эта перестановка уже является последней для 5,4, а 1 - она отсортирована в порядке убывания.
- Таким образом, вам придется заменить «2» на что-то большее - фактически, на наименьшее число, большее, чем в части «541», а именно на 4.
- Теперь, когда вы решили, что перестановка начнется с «34», остальные числа должны быть в порядке возрастания, поэтому ответ будет «34125».
Алгоритм состоит в том, чтобы реализовать именно эту цепочку рассуждений:
- Найдите самый длинный «хвост» в порядке убывания. (Часть "541".)
- Измените число непосредственно перед хвостом («2») на наименьшее число, большее, чем в хвосте (4).
- Отсортируйте хвост в порядке возрастания.
Вы можете выполнить (1.) эффективно, начав с конца и вернувшись назад, если предыдущий элемент не меньше текущего. Вы можете сделать (2.), просто поменяв местами «4» на «2», так что у вас будет «34521». Как только вы это сделаете, вы можете избежать использования алгоритма сортировки для (3.), потому что хвост был и остается (подумайте об этом), отсортированным в порядке убывания, поэтому его нужно только поменять местами.
Код C ++ делает именно это (посмотрите исходный код в /usr/include/c++/4.0.0/bits/stl_algo.h
вашей системе или прочтите эту статью ); его должно быть просто перевести на ваш язык: [Прочтите «BidirectionalIterator» как «указатель», если вы не знакомы с итераторами C ++. Код возвращается, false
если нет следующей перестановки, т.е. мы уже в порядке убывания.]
template <class BidirectionalIterator>
bool next_permutation(BidirectionalIterator first,
BidirectionalIterator last) {
if (first == last) return false
BidirectionalIterator i = first;
++i;
if (i == last) return false
i = last;
--i;
for(
BidirectionalIterator ii = i--;
if (*i <*ii) {
BidirectionalIterator j = last;
while (!(*i <*--j))
iter_swap(i, j)
reverse(ii, last)
return true
}
if (i == first) {
reverse(first, last)
return false
}
}
}
Может показаться, что на перестановку может потребоваться O (n) раз, но если вы подумаете об этом более внимательно, вы можете доказать, что для всех перестановок в целом требуется O (n!) Времени, так что только O (1) - постоянное время - перестановка.
Хорошо то, что алгоритм работает, даже если у вас есть последовательность с повторяющимися элементами: скажем, с «232254421», он найдет хвост как «54421», поменяет местами «2» и «4» (так «232454221» ), переверните остальные, получив "232412245", что является следующей перестановкой.