Remove the item at the given position in the list, and return it. If
no index is specified, a.pop() removes and returns the last item in
the list. (The square brackets around the i in the method signature
denote that the parameter is optional, not that you should type square
brackets at that position. You will see this notation frequently in
the Python Library Reference.)
If you just want to iterate over these sets of elements rather than construct a separate data structure, consider using iterators to construct a generator expression:
This also depends on if you want to shift the list in place (mutating it), or if you want the function to return a new list. Because, according to my tests, something like this is at least twenty times faster than your implementation that adds two lists:
def shiftInPlace(l, n):
n = n % len(l)
head = l[:n]
l[:n] = []
l.extend(head)
return l
In fact, even adding a l = l[:] to the top of that to operate on a copy of the list passed in is still twice as fast.
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
如果需要创建双端队列对象:
1M次迭代@ 6.853878974914551秒
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)
如果您已经有双端队列对象:
1M次迭代@ 0.12380790710449219秒
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
如果您需要创建nparrays
1M次迭代@ 27.558452129364014秒
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
"""
如果您已经有nparrays:
1M次迭代@ 6.0491721630096436秒
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)
“原地转移”
不需要类型转换
1M次迭代@ 4.819645881652832秒
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))
不需要类型转换
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)
If you’re starting with a list, l.append(l.pop(0)) is the fastest method you can use. This can be shown with time complexity alone:
deque.rotate is O(k) (k=number of elements)
list to deque conversion is O(n)
list.append and list.pop are both O(1)
So if you are starting with deque objects, you can deque.rotate() at the cost of O(k). But, if the starting point is a list, the time complexity of using deque.rotate() is O(n). l.append(l.pop(0) is faster at O(1).
Just for the sake of illustration, here are some sample timings on 1M iterations:
Methods which require type conversion:
deque.rotate with deque object: 0.12380790710449219 seconds (fastest)
deque.rotate with type conversion: 6.853878974914551 seconds
np.roll with nparray: 6.0491721630096436 seconds
np.roll with type conversion: 27.558452129364014 seconds
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
If you need to create deque objects:
1M iterations @ 6.853878974914551 seconds
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)
If you already have deque objects:
1M iterations @ 0.12380790710449219 seconds
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
If you need to create nparrays
1M iterations @ 27.558452129364014 seconds
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
"""
If you already have nparrays:
1M iterations @ 6.0491721630096436 seconds
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)
“Shift in place”
Requires no type conversion
1M iterations @ 4.819645881652832 seconds
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))
Requires no type conversion
1M iterations @ 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)
Your method of slicing the list and concatenating two sub-lists are linear-time operations. I would suggest using pop, which is a constant-time operation, e.g.:
def shift(list, n):
for i in range(n)
temp = list.pop()
list.insert(0, temp)
回答 15
我不知道这是否“有效”,但它也有效:
x =[1,2,3,4]
x.insert(0,x.pop())
编辑:您好,我刚刚发现此解决方案有大问题!考虑以下代码:
classMyClass():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 timesprint'\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!'
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!
I don’t know if this is ‘efficient’, but it also works:
x = [1,2,3,4]
x.insert(0,x.pop())
EDIT: Hello again, I just found a big problem with this solution!
Consider the following code:
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!'
The shift_classlist() method executes the same code as my x.insert(0,x.pop())-solution, otherlist is a list indipendent from the class. After passing the content of otherlist to the MyClass.classlist list, calling the shift_classlist() also changes the otherlist list:
CONSOLE OUTPUT:
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!
I use Python 2.7. I don’t know if thats a bug, but I think it’s more likely that I missunderstood something here.
Note that in python, this approach is horribly inefficient compared to others as it can’t take advantage of native implementations of any of the pieces.
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 4print get_shifted_element(my_list,-2,3)-----> outputs 2
What is the use case? Often, we don’t actually need a fully shifted array –we just need to access a handful of elements in the shifted array.
Getting Python slices is runtime O(k) where k is the slice, so a sliced rotation is runtime N. The deque rotation command is also O(k). Can we do better?
Consider an array that is extremely large (let’s say, so large it would be computationally slow to slice it). An alternative solution would be to leave the original array alone and simply calculate the index of the item that would have existed in our desired index after a shift of some kind.
Accessing a shifted element thus becomes 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
回答 20
以下函数将发送的列表复制到临时列表,以便pop函数不会影响原始列表:
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
测试:
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)
输出:
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]
Following function copies sent list to a templist, so that pop function does not affect the original list:
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
Testing:
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)
Jon Bentley in Programming Pearls (Column 2) describes an elegant and efficient algorithm for rotating an n-element vector x left by i positions:
Let’s view the problem as transforming the array ab into the array
ba, but let’s also assume that we have a function that reverses the
elements in a specified portion of the array. Starting with ab, we
reverse a to get arb, reverse b to get
arbr, and then reverse the whole
thing to get (arbr)r,
which is exactly ba. This results in the following code for
rotation:
reverse(0, i-1)
reverse(i, n-1)
reverse(0, n-1)
This can be translated to Python as follows:
def rotate(x, i):
i %= len(x)
x[:i] = reversed(x[:i])
x[i:] = reversed(x[i:])
x[:] = reversed(x)
return x
For a list X = ['a', 'b', 'c', 'd', 'e', 'f'] and a desired shift value of shiftless than list length, we can define the function list_shift() as below