Reputation: 33
I'm looking for the efficient way to shift last element of the list in python to it's appropriate position. for example if we have list = [1, 3, 4, 5, 6, 2] we should get list = [1, 2, 3, 4, 5, 6]. What I tried doesn't work on desirable way:
def sort1(lng, lst):
if len(lst) != lng:
return
else:
i = -2
last = lst[-1]
for x in lst:
if last < lst[i]:
lst[i] = last
i -= 1
print(lst)
sort1(6,[1,3,4,5,6,2])
It is giving me following result:
[1, 3, 4, 5, 2, 2]
[1, 3, 4, 2, 2, 2]
[1, 3, 2, 2, 2, 2]
[1, 2, 2, 2, 2, 2]
Upvotes: 1
Views: 186
Reputation: 22776
The appropriate method is the insertion sort algorithm, but now we're only doing it for the last item, so here it is:
list = [1, 3, 4, 5, 6, 2] # our list
item = list[len(list) - 1] # last element
i = len(list) - 2 # the starting element to compare the last element to
while item < list[i] and i >= 0: # while place not found and index in range
list[i + 1] = list[i] # move element at i to i+1
i -= 1 # decrement i, so to compare with next left element
list[i + 1] = item # when the loop is completed, we then have our last item's position in i+1
print(list) # this prints [1, 2, 3, 4, 5, 6]
You can read more about the insertion sort algorithm here.
Upvotes: 1
Reputation: 180481
If you have to write your own function you can use enumerate and find either where the ele falls between two values or it is smaller than or equal to the first:
def sor1(lst):
# empty or list with single ele or last ele is larger than second last the list os sorted
if len(lst) < 2 or lst[-1] > lst[-2]:
return lst
last = lst.pop()
# if first value is larger than the last we need to insert last before the first
if lst[0] >= last:
lst.insert(0, last)
return lst
# else find first place where last falls between two values
for ind, ele in enumerate(lst):
if ele <= last <= lst[ind+1]:
lst.insert(ind+1,last)
return lst
There is also a blist library where inserts are 0(log n)
and it outperforms some other python list operations, there are some cases also where the reverse is true so it would depend what you wanted to do:
from blist import blist
lst = blist([1, 3, 4, 5, 6, 2])
Some operations where a blist is more efficient vs a list:
Insertion into or removal from a list O(log n) O(n)
Insertion into or removal from a list O(log n) O(n)
Taking slices of lists O(log n) O(n)
Making shallow copies of lists O(1) O(n)
Changing slices of lists O(log n + log k) O(n+k)
Multiplying a list to make a sparse list O(log k) O(kn)
Maintain a sorted lists with bisect.insort O(log**2 n) O(n)
It does slightly outperform a list using insort_left using python 2.7:
In [32]: %%timeit
lst = list(range(1000000))+[90000]
item = lst.pop()
bisect.insort_left(lst, item)
....:
10 loops, best of 3: 38.5 ms per loop
In [33]: %%timeit
lst = blist(range(1000000))+blist([90000])
item = lst.pop()
bisect.insort_left(lst, item)
....:
10 loops, best of 3: 27.9 ms per loop
Upvotes: 0
Reputation: 126
Not nearly as sexy as @Ashwini's answer, but you can try this.
Instead of:
lst[i] = last
(Which is overwriting your entire array with your last value as you move down)
Do this:
lst[i+1] = lst[i]
(this will shift all the values over)
Then after all the looping:
lst[i+1] = last
(Put the last value in its correct position, which is where i+1 should be)
Upvotes: 0
Reputation: 251096
Pop the item from the list and insert it back using bisect.insort_left
:
>>> import bisect
>>> lst = [1, 3, 4, 5, 6, 2]
>>> item = lst.pop()
>>> bisect.insort_left(lst, item)
>>> lst
[1, 2, 3, 4, 5, 6]
Upvotes: 6