Да, там "следующая перестановка" алгоритм, и это довольно просто также. Стандартная библиотека шаблонов (STL) C++ даже имеет функцию, вызванную next_permutation
.
алгоритм на самом деле находит следующий перестановка - лексикографически следующая. Идея - это: предположите, что Вам дают последовательность, говорите "32541". Какова следующая перестановка?
, Если Вы думаете об этом, Вы будете видеть, что это "34125". И Ваши мысли были, вероятно, чем-то это: В "32 541",
алгоритм должен реализовать точно что цепь рассуждений:
можно сделать (1). эффективно путем запуска в конце и движения назад, пока предыдущий элемент не меньше, чем элемент тока. Можно сделать (2). просто подкачав "4" с '2", таким образом, Вы будете иметь "34521". Как только Вы делаете это, можно избегать использования алгоритма сортировки для (3)., потому что хвост был и все еще (думайте об этом), отсортированный в порядке убывания, таким образом, он только должен быть инвертирован.
код C++ делает точно это (посмотрите на источник в /usr/include/c++/4.0.0/bits/stl_algo.h
в Вашей системе или посмотрите эта статья ); должно быть просто перевести его в Ваш язык: [Считайте "BidirectionalIterator" как "указатель", если Вы незнакомы с итераторами C++. Возвраты кода false
, если существует никакая следующая перестановка, т.е. мы уже в порядке убывания.]
template
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", это нашло бы хвост как "54 421", подкачало бы "2" и "4" (так "232454221"), инвертировало бы остальных, дав "232412245", который является следующей перестановкой.
Самое питоническое решение - задокументировать примеры. Если возможно, укажите, какие операции объект должен поддерживать, чтобы быть приемлемым, а не конкретный тип.
class Host(object):
def __init__(self, name, network_interface)
"""Initialise host with given name and network_interface.
network_interface -- must support the same operations as NetworkInterface
>>> network_interface = NetworkInterface()
>>> host = Host("my_host", network_interface)
"""
...
На этом этапе подключите ваш источник к doctest , чтобы убедиться, что ваши примеры документов продолжают работать в будущем. .
Это сделано намеренно и полезно - это означает, что, когда компилятор знает , что это ReadOnlyCollection
, неподдерживаемые функциональные возможности недоступны для вас, что помогает отвлечь вас из-за сбоя во время выполнения.
Это интересный и относительно необычный шаг, эффективно реализует половину свойства / индексатора неявно, а половину явно.
В отличие от моих предыдущих мыслей, я считаю ReadOnlyCollection
на самом деле явно реализует весь индексатор, но также предоставляет общедоступный индексатор только для чтения. Другими словами, это примерно так:
T IList<T>.this[int index]
{
// Delegate interface implementation to "normal" implementation
get { return this[index]; }
set { throw new NotSupportedException("Collection is read-only."); }
}
public T this[int index]
{
get { return ...; }
}
Никакого волшебства, ReadOnlyCollection
просто имеют разные реализации для собственного индексатора и индексатора, реализующего интерфейс IList
:
public T Item[int index] { get; }
T IList<T>.Item[int index] { get; set; }
Если вы приведете свой список к IList
, вы получите ошибку времени выполнения вместо ошибки компиляции:
((IList<int>)b)[2] = 3;
Изменить:
Чтобы реализовать индексатор в вашем собственном классе, вы используете ключевое слово this
:
public T this[int index] { get { ... } }
T IList<T>.this[int index] { get { ... } set { ... } }
Он явно реализует IList.Items, что делает его закрытым, и вам придется преобразовать его в интерфейс, чтобы достичь его реализации, и реализует новый индексатор this [...] , который используется вместо него, у которого есть только get-accessor.
Если вы приведете коллекцию к IList, ваш код будет компилироваться, но вместо этого завершится с ошибкой во время выполнения.
К сожалению, я не знаю, как это сделать в C #, поскольку написание индексатора на C # включает использование ключевого слова this
, и вы не можете написать это:
T IList<T>.this[int index] { get; set; }