Я предполагаю, что ОП хочет сделать это InPlace.
Ключом к быстрому выполнению операции является минимизация создания списков и укорочение/удлинение списков. Это означает, что мы должны стремиться всегда выполнять присвоение индексов списка 1: 1, поэтому нет L[i:i] = L[a:b]
и не L[a:b] = []
. Использование циклов с insert
и pop
еще хуже, потому что тогда вы сокращаете и удлиняете список много раз. Конкатенационные списки также плохи, потому что вам сначала нужно создать один список для каждой части, а затем создать более крупные и крупные конкатенированные списки, по одному для каждого +
. Поскольку вы хотите сделать это «inplace», вам придется назначить сгенерированный список L[:]
в конце.
# items: 0 | 1 2 3 | 4 5 6 7 | 8 9
# a span1 b span2 c
# pos: 1 4 8
# Result:
# 0 | 4 5 6 7 | 1 2 3 | 8 9
# a span2 span2 c
Давайте сначала сделаем наблюдение. Если a = start
, b = end = start + length
и c
- это позиция вставки, то операция, которую мы хотим сделать, - разрезать по маркам |
и обмену span1
и span2
.Но если b = start
и c = end
и a
- позиция вставки, то мы также хотите обменять span1
и span2
. Поэтому в нашей функции мы имеем дело с двумя последовательными сегментами, которые должны быть заменены.
Мы не можем полностью избежать создания новых списков, потому что нам нужно хранить перекрывающиеся значения при перемещении материала. Мы можем, однако сделаем список как можно короче, выбрав, какой из двух интервалов хранить во временном списке.
def inplace_shift(L, start, length, pos):
if pos > start + length:
(a, b, c) = (start, start + length, pos)
elif pos < start:
(a, b, c) = (pos, start, start + length)
else:
raise ValueError("Cannot shift a subsequence to inside itself")
if not (0 <= a < b < c <= len(L)):
msg = "Index check 0 <= {0} < {1} < {2} <= {3} failed."
raise ValueError(msg.format(a, b, c, len(L)))
span1, span2 = (b - a, c - b)
if span1 < span2:
tmp = L[a:b]
L[a:a + span2] = L[b:c]
L[c - span1:c] = tmp
else:
tmp = L[b:c]
L[a + span2:c] = L[a:b]
L[a:a + span2] = tmp
Кос, кажется, сделал ошибку в своих таймингах, так что я переделал их свой код после коррекции аргументов (вычислений end
от start
и length
), и этих результатов, от самого медленного до самого быстрого.
Nick Craig-Wood: 100 loops, best of 3: 8.58 msec per loop
vivek: 100 loops, best of 3: 4.36 msec per loop
PaulP.R.O. (deleted?): 1000 loops, best of 3: 838 usec per loop
unbeli: 1000 loops, best of 3: 264 usec per loop
lazyr: 10000 loops, best of 3: 44.6 usec per loop
Я не проверял, что любой из других подходов дает правильные результаты.
Как вы знаете, что с, d, е являются элементы, которые вы хотите переместить? Являются ли они фиксированными, или вы проверяете значения? –
Что делать, если подсписщик перекрывает точку вставки? –
Это невозможно, ответ @ lazyr также предотвращает это. – TTT