Cách hiệu quả để xoay danh sách trong python


263

Cách hiệu quả nhất để xoay danh sách trong python là gì? Ngay bây giờ tôi có một cái gì đó như thế này:

>>> def rotate(l, n):
...     return l[n:] + l[:n]
... 
>>> l = [1,2,3,4]
>>> rotate(l,1)
[2, 3, 4, 1]
>>> rotate(l,2)
[3, 4, 1, 2]
>>> rotate(l,0)
[1, 2, 3, 4]
>>> rotate(l,-1)
[4, 1, 2, 3]

Có cách nào tốt hơn?


12
Điều này không thực sự thay đổi khi các ngôn ngữ khác (Perl, Ruby) sử dụng thuật ngữ này. Đây là xoay. Có lẽ câu hỏi nên được cập nhật cho phù hợp?
Vincent Fourmond

@dzhelil Tôi thực sự thích giải pháp ban đầu của bạn vì nó không giới thiệu đột biến
juanchito


2
Tôi nghĩ rotatelà từ đúng, không phải shift.
tiền mã hóa

2
Các thực câu trả lời đúng, là bạn nên không bao giờ được quay trong danh sách ở nơi đầu tiên. Tạo một biến "con trỏ" đến vị trí hợp lý trong danh sách của bạn, nơi bạn muốn "đầu" hoặc "đuôi" và thay đổi biến đó thay vì di chuyển bất kỳ mục nào trong danh sách. Tra cứu toán tử "modulo"% để biết cách hiệu quả để "bọc" con trỏ của bạn xung quanh đầu và cuối danh sách.
cnd

Câu trả lời:


280

A collections.dequeđược tối ưu hóa để kéo và đẩy trên cả hai đầu. Họ thậm chí có một rotate()phương pháp chuyên dụng .

from collections import deque
items = deque([1, 2])
items.append(3)        # deque == [1, 2, 3]
items.rotate(1)        # The deque is now: [3, 1, 2]
items.rotate(-1)       # Returns deque to original state: [1, 2, 3]
item = items.popleft() # deque == [2, 3]

8
Đối với độc giả trong tương lai: collections.deque rotate()nhanh hơn cắt lát theo wiki.python.org/moin/TimeComplexity
Geoff

2
Nhưng hãy lưu ý, việc sử dụng deque.rotateyêu cầu chuyển đổi loại thành dequeđối tượng trước, tốc độ chậm hơn l.append(l.pop(0)). Vì vậy, nếu bạn có một đối tượng deque để bắt đầu, chắc chắn nó là nhanh nhất. Nếu không, sử dụng l.append(l.pop(0)).
Purrell

8
Để giải thích, deque.rotatelà O (k) nhưng gõ chuyển đổi từ danh sách sang deque là O (n) . Vì vậy, nếu bạn bắt đầu với một danh sách, sử dụng deque.rotate là O (n) + O (k) = O (n). l.append(l.pop(0))mặt khác là O (1).
Purrell

3
@Purrell, xuất hiện mục trước là O (n). Trong wiki.python.org/moin/TimeComplexity, nó được liệt kê là O (k) và k là số phần tử trong danh sách theo sau mục được bật, bởi vì cấu trúc dữ liệu sẽ dịch chuyển tất cả các phần tử sau về phía trước danh sách. Chỉ phần tử cuối cùng có thể được bật trong thời gian O (1) vì lý do này.
Kirk Boyer

88

Còn việc chỉ sử dụng pop(0)thì sao?

list.pop([i])

Xóa mục tại vị trí đã cho trong danh sách và trả lại. Nếu không có chỉ mục nào được chỉ định, hãy a.pop()xóa và trả về mục cuối cùng trong danh sách. (Dấu ngoặc vuông xung quanh ichữ ký phương thức biểu thị rằng tham số là tùy chọn, không phải là bạn nên nhập dấu ngoặc vuông ở vị trí đó. Bạn sẽ thấy ký hiệu này thường xuyên trong Tham chiếu thư viện Python.)


16
Nhưng sẽ không tốn O (k) cho việc loại bỏ từng phần tử trong danh sách trong đó k là số phần tử còn lại. Vì vậy, tổng thời gian sẽ là O (n ^ 2) wiki.python.org/moin/TimeComplexity
Pramod

5
Điều này không thực sự trả lời câu hỏi. Câu hỏi không phải là về việc trả lại các mục theo thứ tự, mà là về việc tạo một danh sách mới theo thứ tự khác.
dùng650261

5
không, câu trả lời cho câu hỏi sử dụng pop sẽ là l.append(l.pop(0). Mà nếu tôi không nhầm là O (1).
Purrell

4
list.pop gọi nội bộ list_ass_slice, sử dụng memmove để di chuyển rất nhanh tất cả các mục, nhưng nó vẫn là O (n). Xem github.com/python/cpython/blob/master/Objects/listobject.cwiki.python.org/moin/TimeComplexity . Mục duy nhất có thể được xóa khỏi danh sách python trong thời gian không đổi là mục cuối cùng.
DRayX

2
Bị hạ bệ. Từ docs.python.org/3/tutorial/, Cũng có thể sử dụng một danh sách như một hàng đợi, trong đó phần tử đầu tiên được thêm vào là phần tử đầu tiên được lấy ra (đầu tiên, đầu tiên, ra trước); tuy nhiên, danh sách không hiệu quả cho mục đích này. Mặc dù các phần bổ sung và bật lên từ cuối danh sách rất nhanh, nhưng việc chèn hoặc bật từ đầu danh sách là chậm (vì tất cả các yếu tố khác phải được thay đổi bởi một).
SantaXL

59

Numpy có thể làm điều này bằng cách sử dụng rolllệnh:

>>> import numpy
>>> a=numpy.arange(1,10) #Generate some data
>>> numpy.roll(a,1)
array([9, 1, 2, 3, 4, 5, 6, 7, 8])
>>> numpy.roll(a,-1)
array([2, 3, 4, 5, 6, 7, 8, 9, 1])
>>> numpy.roll(a,5)
array([5, 6, 7, 8, 9, 1, 2, 3, 4])
>>> numpy.roll(a,9)
array([1, 2, 3, 4, 5, 6, 7, 8, 9])

1
Điều tôi thích về SO là đôi khi xuống các câu trả lời, bạn có thể tìm thấy một số kho báu mới tuyệt vời như thế này :)
noamgot 16/12/19

Điều này, khi tôi thử nghiệm nó, rất, rất chậm
Peter Harrison

@PeterHarrison: Vì bạn không cung cấp chi tiết kiểm tra nên thật khó để biết ý của bạn là gì. Câu trả lời này cung cấp chi tiết kiểm tra đầy đủ và so sánh thời gian.
Richard

33

Nó phụ thuộc vào những gì bạn muốn có xảy ra khi bạn làm điều này:

>>> shift([1,2,3], 14)

Bạn có thể muốn thay đổi:

def shift(seq, n):
    return seq[n:]+seq[:n]

đến:

def shift(seq, n):
    n = n % len(seq)
    return seq[n:] + seq[:n]

5
NB: Điều này sẽ sụp đổ cho danh sách trống.
meawoppl

n = n% len (seq) return = seq [-n:] + seq [: - n]
user3303020

Bạn có thể giải thích tại sao n = n% len (seq) không?
AerysS

16

Cách đơn giản nhất tôi có thể nghĩ ra:

a.append(a.pop(0))

3
Đây là cách nhanh nhất cho danh sách. collections.dequenhanh hơn nhưng đối với hầu hết các trường hợp phổ biến về độ dài danh sách trên một lần lặp hoặc bất kỳ trường hợp lặp lại nào, a.append(a.pop(0))sẽ nhanh hơn chuyển đổi loại thành deque
Purrell

@runDOSrun câu trả lời hoàn hảo cho câu hỏi này đáng buồn đóng lại như một bản sao. Có lẽ bạn sẽ bỏ phiếu để mở lại nó?
Sói

15

Nếu bạn chỉ muốn lặp lại các bộ phần tử này thay vì xây dựng cấu trúc dữ liệu riêng biệt, hãy xem xét sử dụng các trình lặp để xây dựng biểu thức trình tạo:

def shift(l,n):
    return itertools.islice(itertools.cycle(l),n,n+len(l))

>>> list(shift([1,2,3],1))
[2, 3, 1]

11

Điều này cũng phụ thuộc vào việc bạn muốn thay đổi danh sách tại chỗ (thay đổi nó) hay nếu bạn muốn hàm trả về một danh sách mới. Bởi vì, theo các thử nghiệm của tôi, một cái gì đó như thế này nhanh hơn ít nhất hai mươi lần so với việc bạn thực hiện có thêm hai danh sách:

def shiftInPlace(l, n):
    n = n % len(l)
    head = l[:n]
    l[:n] = []
    l.extend(head)
    return l

Trong thực tế, ngay cả việc thêm một l = l[:]vào đầu của nó để hoạt động trên một bản sao của danh sách được truyền vào vẫn nhanh gấp đôi.

Triển khai khác nhau với một số thời gian tại http://gist.github.com/288272


3
Thay vì l[:n] = []tôi sẽ đi cho del l[:n]. Chỉ là một sự thay thế.
tzot

1
Ồ, vâng, del cũ tốt. Tôi thường quên về del; danh sách hoạt động đó là một tuyên bố, không phải là một phương pháp. Có phải py3k đã thay đổi điều đó, hay chúng ta vẫn hiểu?
keturn

2
@keturn: delvẫn là một tuyên bố trong Py3. Tuy nhiên x.__delitem__(y) <==> del x[y], vì vậy, nếu bạn thích sử dụng các phương thức, l.__delitem__(slice(n))cũng tương đương và hoạt động trong cả 2 & 3.
martineau

9

Chỉ cần một số lưu ý về thời gian:

Nếu bạn đang bắt đầu với một danh sách, l.append(l.pop(0))là phương pháp nhanh nhất bạn có thể sử dụng. Điều này có thể được hiển thị với sự phức tạp thời gian một mình:

  • deque.rotate là O (k) (k = số phần tử)
  • danh sách để chuyển đổi deque là O (n)
  • list.append và list.pop đều là O (1)

Vì vậy, nếu bạn đang bắt đầu với dequecác đối tượng, bạn có thể deque.rotate()với chi phí O (k). Nhưng, nếu điểm bắt đầu là một danh sách, thì độ phức tạp thời gian sử dụng deque.rotate()là O (n). l.append(l.pop(0)nhanh hơn ở O (1).

Chỉ để minh họa, đây là một số thời gian mẫu trên các lần lặp 1M:

Các phương thức yêu cầu chuyển đổi loại:

  • deque.rotatevới đối tượng deque: 0.12380790710449219 giây (nhanh nhất)
  • deque.rotatevới chuyển đổi loại: 6.853878974914551 giây
  • np.rollvới nparray: 6.0491721630096436 giây
  • np.rollvới chuyển đổi loại: 27.558452129364014 giây

Liệt kê các phương pháp được đề cập ở đây:

  • l.append(l.pop(0)): 0,32483696937561035 giây (nhanh nhất)
  • " shiftInPlace": 4.819645881652832 giây
  • ...

Mã thời gian được sử dụng là dưới đây.


bộ sưu tập

Cho thấy việc tạo deques từ danh sách là O (n):

from collections import deque
import big_o

def create_deque_from_list(l):
     return deque(l)

best, others = big_o.big_o(create_deque_from_list, lambda n: big_o.datagen.integers(n, -100, 100))
print best

# --> Linear: time = -2.6E-05 + 1.8E-08*n

Nếu bạn cần tạo các đối tượng deque:

Lặp lại 1M @ 6.853878974914551 giây

setup_deque_rotate_with_create_deque = """
from collections import deque
import random
l = [random.random() for i in range(1000)]
"""

test_deque_rotate_with_create_deque = """
dl = deque(l)
dl.rotate(-1)
"""
timeit.timeit(test_deque_rotate_with_create_deque, setup_deque_rotate_with_create_deque)

Nếu bạn đã có các đối tượng deque:

Lặp lại 1M @ 0.12380790710449219 giây

setup_deque_rotate_alone = """
from collections import deque
import random
l = [random.random() for i in range(1000)]
dl = deque(l)
"""

test_deque_rotate_alone= """
dl.rotate(-1)
"""
timeit.timeit(test_deque_rotate_alone, setup_deque_rotate_alone)

np.roll

Nếu bạn cần tạo ra các ngày

Lặp lại 1M @ 27.558452129364014 giây

setup_np_roll_with_create_npa = """
import numpy as np
import random
l = [random.random() for i in range(1000)]
"""

test_np_roll_with_create_npa = """
np.roll(l,-1) # implicit conversion of l to np.nparray
"""

Nếu bạn đã có ngày:

Lặp lại 1M @ 6.0491721630096436 giây

setup_np_roll_alone = """
import numpy as np
import random
l = [random.random() for i in range(1000)]
npa = np.array(l)
"""

test_roll_alone = """
np.roll(npa,-1)
"""
timeit.timeit(test_roll_alone, setup_np_roll_alone)

"Chuyển tại chỗ"

Không yêu cầu chuyển đổi loại

Lặp lại 1M @ 4.819645881652832 giây

setup_shift_in_place="""
import random
l = [random.random() for i in range(1000)]
def shiftInPlace(l, n):
    n = n % len(l)
    head = l[:n]
    l[:n] = []
    l.extend(head)
    return l
"""

test_shift_in_place="""
shiftInPlace(l,-1)
"""

timeit.timeit(test_shift_in_place, setup_shift_in_place)

l.append (l.pop (0))

Không yêu cầu chuyển đổi loại

Lặp lại 1M @ 0,32483696937561035

setup_append_pop="""
import random
l = [random.random() for i in range(1000)]
"""

test_append_pop="""
l.append(l.pop(0))
"""
timeit.timeit(test_append_pop, setup_append_pop)

2
trong khi list.pop () là một hoạt động liên tục, list.pop (0) thì không . Nó chạy trong thời gian tuyến tính liên quan đến chiều dài danh sách. Bạn có thể kiểm tra điều đó bằng cách sửa đổi thiết lập thời gian của mình:l = [random.random() for i in range(100000)]
emu

1
list.pop không phải là một hoạt động thời gian liên tục. list.pop chạy trong thời gian O (k) trong đó k là số phần tử vượt qua phần tử bị xóa, vì vậy list.pop (0) là O (n). Trong nội bộ, list.pop sử dụng list_ass_slice, sử dụng memmove để di chuyển các mục nhanh hơn bao giờ hết với python, nhưng đối với các danh sách dài thì vẫn rất tốn thời gian. Xem github.com/python/cpython/blob/master/Objects/listobject.cwiki.python.org/moin/TimeComplexity
DRayX

Cảm ơn thời gian (và ý kiến ​​@emu). Vì vậy, chúng ta có thể nói rằng đó l.append(l.pop(0))là hiệu suất tốt nhất để thay đổi danh sách ngắn (khoảng 7 yếu tố) theo một?
Sói

Một lần nữa, liên quan l.append(l.pop(0))như một câu trả lời: Câu hỏi này được đóng lại như một bản sao. Có lẽ bạn sẽ bỏ phiếu để mở lại nó?
Sói

8

Tôi cũng đã quan tâm đến điều này và so sánh một số giải pháp được đề xuất với perfplot (một dự án nhỏ của tôi).

Hóa ra

for _ in range(n):
    data.append(data.pop(0))

cho đến nay phương pháp nhanh nhất cho sự thay đổi nhỏn .

Đối với lớn hơn n,

data[n:] + data[:n]

không tệ

Về cơ bản, perfplot thực hiện sự thay đổi để tăng các mảng lớn và đo thời gian. Đây là kết quả:

shift = 1:

nhập mô tả hình ảnh ở đây

shift = 100:

nhập mô tả hình ảnh ở đây


Mã để tái tạo cốt truyện:

import numpy
import perfplot
import collections


shift = 100


def list_append(data):
    return data[shift:] + data[:shift]


def shift_concatenate(data):
    return numpy.concatenate([data[shift:], data[:shift]])


def roll(data):
    return numpy.roll(data, -shift)


def collections_deque(data):
    items = collections.deque(data)
    items.rotate(-shift)
    return items


def pop_append(data):
    for _ in range(shift):
        data.append(data.pop(0))
    return data


perfplot.save(
    "shift100.png",
    setup=lambda n: numpy.random.rand(n).tolist(),
    kernels=[list_append, roll, shift_concatenate, collections_deque, pop_append],
    n_range=[2 ** k for k in range(7, 20)],
    logx=True,
    logy=True,
    xlabel="len(data)",
)

Công cụ đẹp bạn xây dựng. Liên quan l.append(l.pop(0))như một câu trả lời: Câu hỏi này được đóng lại như một bản sao. Có lẽ bạn sẽ bỏ phiếu để mở lại nó?
Sói

4

Có thể một ringbuffer là phù hợp hơn. Nó không phải là một danh sách, mặc dù có khả năng nó có thể hoạt động đủ như một danh sách cho mục đích của bạn.

Vấn đề là hiệu quả của sự thay đổi trong danh sách là O (n), điều này trở nên quan trọng đối với danh sách đủ lớn.

Dịch chuyển trong ringbuffer chỉ đơn giản là cập nhật vị trí đầu là O (1)


4

Để thực hiện bất biến, bạn có thể sử dụng một cái gì đó như thế này:

def shift(seq, n):
    shifted_seq = []
    for i in range(len(seq)):
        shifted_seq.append(seq[(i-n) % len(seq)])
    return shifted_seq

print shift([1, 2, 3, 4], 1)

3

Nếu hiệu quả là mục tiêu của bạn, (chu kỳ? Bộ nhớ?), Bạn có thể tốt hơn khi xem mô-đun mảng: http://docs.python.org/l Library / marray.html

Mảng không có chi phí chung của danh sách.

Theo như danh sách thuần túy, những gì bạn có là tốt như bạn có thể hy vọng làm được.


3

Tôi nghĩ rằng bạn đang tìm kiếm điều này:

a.insert(0, x)

Tôi không thấy mối quan hệ giữa câu hỏi và câu trả lời của bạn. Bạn có thể vui lòng giải thích nó?
Sói

2

Một cách khác:

def move(arr, n):
    return [arr[(idx-n) % len(arr)] for idx,_ in enumerate(arr)]

1

Tôi lấy mô hình chi phí này làm tài liệu tham khảo:

http://scripts.mit.edu/~6.006/fall07/wiki/index.php?title=Python_Cost_Model

Phương pháp cắt danh sách của bạn và ghép hai danh sách phụ là các hoạt động theo thời gian tuyến tính. Tôi sẽ đề nghị sử dụng pop, đó là một hoạt động liên tục, ví dụ:

def shift(list, n):
    for i in range(n)
        temp = list.pop()
        list.insert(0, temp)

2
cập nhật: lấy điều này làm tài liệu tham khảo tốt hơn: wiki.python.org/moin/TimeComplexity , sử dụng collections.dequeuepop và appendleft, cả hai đều là O (1) ops. Trong câu trả lời đầu tiên của tôi ở trên, chèn là O (n).
herrfz

1
nên làcollections.deque
herrfz

1

Tôi không biết nếu điều này là 'hiệu quả', nhưng nó cũng hoạt động:

x = [1,2,3,4]
x.insert(0,x.pop())

EDIT: Xin chào lần nữa, tôi vừa tìm thấy một vấn đề lớn với giải pháp này! Hãy xem xét các mã sau đây:

class MyClass():
    def __init__(self):
        self.classlist = []

    def shift_classlist(self): # right-shift-operation
        self.classlist.insert(0, self.classlist.pop())

if __name__ == '__main__':
    otherlist = [1,2,3]
    x = MyClass()

    # this is where kind of a magic link is created...
    x.classlist = otherlist

    for ii in xrange(2): # just to do it 2 times
        print '\n\n\nbefore shift:'
        print '     x.classlist =', x.classlist
        print '     otherlist =', otherlist
        x.shift_classlist() 
        print 'after shift:'
        print '     x.classlist =', x.classlist
        print '     otherlist =', otherlist, '<-- SHOULD NOT HAVE BIN CHANGED!'

Phương thức shift_grouplist () thực thi cùng mã với giải pháp x.insert (0, x.pop ()) của tôi, danh sách khác là một danh sách phụ thuộc từ lớp. Sau khi chuyển nội dung của danh sách khác vào danh sách MyClass. Classlist, việc gọi shift_grouplist () cũng thay đổi danh sách danh sách khác:

TIÊU THỤ ĐẦU TƯ:

before shift:
     x.classlist = [1, 2, 3]
     otherlist = [1, 2, 3]
after shift:
     x.classlist = [3, 1, 2]
     otherlist = [3, 1, 2] <-- SHOULD NOT HAVE BIN CHANGED!



before shift:
     x.classlist = [3, 1, 2]
     otherlist = [3, 1, 2]
after shift:
     x.classlist = [2, 3, 1]
     otherlist = [2, 3, 1] <-- SHOULD NOT HAVE BIN CHANGED!

Tôi sử dụng Python 2.7. Tôi không biết đó có phải là một lỗi không, nhưng tôi nghĩ nhiều khả năng là tôi đã hiểu nhầm điều gì đó ở đây.

Có ai trong số các bạn biết tại sao điều này xảy ra?


2
Điều đó xảy ra bởi vì x.classlist = otherlistlàm cho x.classlisttham chiếu đến cùng một danh sách otherlistvà sau đó khi bạn gọi x.shift_classlist()nó sẽ làm thay đổi danh sách và vì cả hai tên đều tham chiếu đến cùng một đối tượng danh sách. Cả hai tên dường như thay đổi vì chúng chỉ là bí danh cho cùng một đối tượng. Sử dụng x.classlist = otherlist[:]thay thế để chỉ định một bản sao của danh sách.
Dan D.

Này wow! Cảm ơn rât nhiều! Tôi thực sự không biết điều đó và thật tuyệt khi biết điều đó! :)
wese3112

1

Phương pháp sau đây là O (n) tại chỗ với bộ nhớ phụ không đổi:

def rotate(arr, shift):
  pivot = shift % len(arr)
  dst = 0
  src = pivot
  while (dst != src):
    arr[dst], arr[src] = arr[src], arr[dst]
    dst += 1
    src += 1
    if src == len(arr):
      src = pivot
    elif dst == pivot:
      pivot = src

Lưu ý rằng trong python, cách tiếp cận này không hiệu quả khủng khiếp so với các phương pháp khác vì nó không thể tận dụng các triển khai nguyên gốc của bất kỳ phần nào.


tốt, thực sự bạn có thể sử dụng list.pop và list.append. Đó không phải là lỗi của ngôn ngữ mà bạn đã viết hàm 12 dòng là O (n), khi bạn có thể vừa viết "l.append (l.pop (0))" là thời gian không đổi.
Purrell

l.append (l.pop (0)) là O (n) (l.pop (0) phải dịch chuyển mọi phần tử), do đó nếu bạn muốn thay đổi giá trị m thì độ phức tạp thực sự là O (n * m). Độ phức tạp của thuật toán tôi cung cấp là O (n) bất kể số lượng ca. Trong thực tế, điều này là chậm vì rất nhiều logic được thực hiện trong các python ops thay vì C (list.pop được triển khai trong c, xem github.com/python/cpython/blob/master/Objects/listobject.c ).
DRayX

1

Tôi có điều tương tự. Ví dụ: để thay đổi bởi hai ...

def Shift(*args):
    return args[len(args)-2:]+args[:len(args)-2]

1

Tôi nghĩ rằng bạn đã có cách hiệu quả nhất

def shift(l,n):
    n = n % len(l)  
    return l[-U:] + l[:-U]

0

Trường hợp sử dụng là gì? Thông thường, chúng ta thực sự không cần một mảng được thay đổi hoàn toàn - chúng ta chỉ cần truy cập vào một số phần tử trong mảng được dịch chuyển.

Bắt các lát Python là thời gian chạy O (k) trong đó k là lát cắt, do đó, một lát cắt được cắt là thời gian chạy N. Lệnh xoay deque cũng là O (k). Chúng ta có thể làm tốt hơn không?

Hãy xem xét một mảng cực lớn (giả sử, lớn đến mức nó sẽ bị tính toán chậm để cắt nó). Một giải pháp thay thế sẽ là để lại mảng ban đầu và chỉ cần tính toán chỉ số của mặt hàng tồn tại trong chỉ mục mong muốn của chúng tôi sau một sự thay đổi nào đó.

Truy cập một phần tử thay đổi do đó trở thành O (1).

def get_shifted_element(original_list, shift_to_left, index_in_shifted):
    # back calculate the original index by reversing the left shift
    idx_original = (index_in_shifted + shift_to_left) % len(original_list)
    return original_list[idx_original]

my_list = [1, 2, 3, 4, 5]

print get_shifted_element(my_list, 1, 2) ----> outputs 4

print get_shifted_element(my_list, -2, 3) -----> outputs 2 

0

Theo dõi các bản sao chức năng được gửi đến một templist, để chức năng pop không ảnh hưởng đến danh sách ban đầu:

def shift(lst, n, toreverse=False):
    templist = []
    for i in lst: templist.append(i)
    if toreverse:
        for i in range(n):  templist = [templist.pop()]+templist
    else:
        for i in range(n):  templist = templist+[templist.pop(0)]
    return templist

Kiểm tra:

lst = [1,2,3,4,5]
print("lst=", lst)
print("shift by 1:", shift(lst,1))
print("lst=", lst)
print("shift by 7:", shift(lst,7))
print("lst=", lst)
print("shift by 1 reverse:", shift(lst,1, True))
print("lst=", lst)
print("shift by 7 reverse:", shift(lst,7, True))
print("lst=", lst)

Đầu ra:

lst= [1, 2, 3, 4, 5]
shift by 1: [2, 3, 4, 5, 1]
lst= [1, 2, 3, 4, 5]
shift by 7: [3, 4, 5, 1, 2]
lst= [1, 2, 3, 4, 5]
shift by 1 reverse: [5, 1, 2, 3, 4]
lst= [1, 2, 3, 4, 5]
shift by 7 reverse: [4, 5, 1, 2, 3]
lst= [1, 2, 3, 4, 5]

0

Jon Bentley trong Lập trình Ngọc trai (Cột 2) mô tả một thuật toán thanh lịch và hiệu quả để xoay một nvectơ tầng xbên trái của icác vị trí:

Chúng ta hãy xem vấn đề là chuyển đổi mảng abthành mảng ba, nhưng cũng giả sử rằng chúng ta có một hàm đảo ngược các phần tử trong một phần xác định của mảng. Bắt đầu với ab, chúng tôi đảo ngược ađể có được , đảo ngược để có được , và sau đó đảo ngược toàn bộ điều cần nhận , đó là chính xác . Điều này dẫn đến mã sau đây cho phép quay:arbbarbr(arbr)rba

reverse(0, i-1)
reverse(i, n-1)
reverse(0, n-1)

Điều này có thể được dịch sang Python như sau:

def rotate(x, i):
    i %= len(x)
    x[:i] = reversed(x[:i])
    x[i:] = reversed(x[i:])
    x[:] = reversed(x)
    return x

Bản giới thiệu:

>>> def rotate(x, i):
...     i %= len(x)
...     x[:i] = reversed(x[:i])
...     x[i:] = reversed(x[i:])
...     x[:] = reversed(x)
...     return x
... 
>>> rotate(list('abcdefgh'), 1)
['b', 'c', 'd', 'e', 'f', 'g', 'h', 'a']
>>> rotate(list('abcdefgh'), 3)
['d', 'e', 'f', 'g', 'h', 'a', 'b', 'c']
>>> rotate(list('abcdefgh'), 8)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
>>> rotate(list('abcdefgh'), 9)
['b', 'c', 'd', 'e', 'f', 'g', 'h', 'a']

0

Đối với danh sách X = ['a', 'b', 'c', 'd', 'e', 'f']và giá trị dịch chuyển mong muốn shift nhỏ hơn độ dài danh sách , chúng ta có thể xác định hàm list_shift()như dưới đây

def list_shift(my_list, shift):
    assert shift < len(my_list)
    return my_list[shift:] + my_list[:shift]

Ví dụ,

list_shift(X,1)trả ['b', 'c', 'd', 'e', 'f', 'a'] list_shift(X,3)về['d', 'e', 'f', 'a', 'b', 'c']


1
Đó chính xác là những gì OP có. Bạn chỉ cần thay đổi tên và thêm một xác nhận.
RufusVS

Hàm list_shifttrong câu trả lời của bạn giống hệt với hàm shifttrong câu hỏi ban đầu, vì vậy đây không phải là câu trả lời cho câu hỏi thực tế: "Có cách nào tốt hơn không?"
RufusVS

0
def solution(A, K):
    if len(A) == 0:
        return A

    K = K % len(A)

    return A[-K:] + A[:-K]

# use case
A = [1, 2, 3, 4, 5, 6]
K = 3
print(solution(A, K))

Ví dụ, được đưa ra

A = [3, 8, 9, 7, 6]
K = 3

chức năng sẽ trở lại [9, 7, 6, 3, 8]. Ba vòng quay đã được thực hiện:

[3, 8, 9, 7, 6] -> [6, 3, 8, 9, 7]
[6, 3, 8, 9, 7] -> [7, 6, 3, 8, 9]
[7, 6, 3, 8, 9] -> [9, 7, 6, 3, 8]

Cho một ví dụ khác, được đưa ra

A = [0, 0, 0]
K = 1

chức năng sẽ trở lại [0, 0, 0]

Được

A = [1, 2, 3, 4]
K = 4

chức năng sẽ trở lại [1, 2, 3, 4]


0

Tôi đã tìm kiếm giải pháp tại chỗ cho vấn đề này. Điều này giải quyết mục đích trong O (k).

def solution(self, list, k):
    r=len(list)-1
    i = 0
    while i<k:
        temp = list[0]
        list[0:r] = list[1:r+1]
        list[r] = temp
        i+=1
    return list

-3

cho chức năng tương tự như sự thay đổi trong các ngôn ngữ khác:

def shift(l):
    x = l[0]
    del(l[0])
    return x

1
-1: Điều này đang làm một cái gì đó khác với những gì được hỏi và BTW cũng tương đương vớiL.pop(0)
6502
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.