简体   繁体   中英

Shift last element of the list

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]

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]

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 .

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)

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM