Так, це алгоритм «наступна перестановка», і це досить просто занадто. Стандартна бібліотека шаблонів 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", що є наступною перестановкою.