Implementation of LinkedList in python __getitem__() method

雨燕双飞 提交于 2019-12-05 16:51:47

You can do this, for example:

def __getitem__(self, index):
    if isinstance(index, int):
        if index < 0:
            index = len(self) + index
        # check if `index` is valid
        # search for the element as you're currently doing.
    elif isinstance(index, slice):
        return [self[i] for i in range(len(self))[index]]
    else:
        raise ValueError(f'Linked list cannot be indexed with values of type {type(index)}')

UPDATE: the code above is very concise, but it's also tremendously slow. If I'm not mistaken, it's a bit better than O(n**2), while the code below is at least 71.58 times faster (doing linkedListWith500Elements[::-1]), and it should be about O(n)!

This should be way faster because it doesn't iterate through the list each time to retrieve the next element of the slice:

class LinkedList:
    ...

    def __iter__(self):
        temp = self.__head
        while temp is not None:
            yield temp.value
            temp = temp.ref

    def __getitem__(self, index):
        if isinstance(index, int):
            if index < 0:
                index = len(self) + index

            for i, value in enumerate(self):
                if i == index:
                    return value
            raise IndexError(f'{type(self).__name__} index {index} out of range(0, {len(self)})')
        elif isinstance(index, slice):
            rangeOfIndices = range(len(self))[index]
            isRangeIncreasing = rangeOfIndices.start <= rangeOfIndices.stop + 1 and rangeOfIndices.step > 0


            rangeOfIndices = iter(rangeOfIndices) if isRangeIncreasing else reversed(rangeOfIndices)

            retval = []  # you can preallocate this...
            updateRetval = retval.append if isRangeIncreasing else (lambda value: retval.insert(0, value))  # ...and change this accordingly, although I haven't tested whether it'll be faster

            try:
                searchingForIndex = next(rangeOfIndices)
            except StopIteration:
                return retval

            temp = self.__head   
            for i, element in enumerate(self):
                if temp is None:
                    break

                if i == searchingForIndex:
                    updateRetval(temp.value)

                    try:
                        searchingForIndex = next(rangeOfIndices)
                    except StopIteration:
                        return retval

                temp = temp.ref

            return retval
        raise ValueError(f'{type(self).__name__} can only be indexed with integers or slices (not {type(index)})')

Preallocating the list should be around 22% faster:

...
rangeOfIndices = range(len(self))[index]
isRangeIncreasing = rangeOfIndices.start <= rangeOfIndices.stop + 1 and rangeOfIndices.step > 0

# preallocate the list...     
retval = [None] * len(rangeOfIndices)   

if isRangeIncreasing:
    retvalIndex = 0
    rangeOfIndices = iter(rangeOfIndices)
    # ...and use a different update function
    def updateRetval(value):
        nonlocal retvalIndex
        retval[retvalIndex] = value
        retvalIndex += 1
else:
    retvalIndex = len(retval) - 1
    rangeOfIndices = reversed(rangeOfIndices)
    def updateRetval(value):
        nonlocal retvalIndex
        retval[retvalIndex] = value
        retvalIndex -= 1

try:
...

To solve this with the least amount of code, you can first convert your linked list into a python list and then slice that python list.

But first you have to rename your method getList(self) to __iter__(self). Thats the canonical name anyways.

Now __getitem__ becomes one line:

def __getitem__(self, index):
    return list(self)[index]

This is not very space efficient, since it duplicates your list.

Edit:

Here is a more efficient solution. I assume again that getList(self) is renamed to __iter__.

def __getitem__(self, index):
    # creates all requested indices or just one int
    indices = range(len(self))[index]  # constant time and space

    if isinstance(indices, int):
        return next(value for i, value in enumerate(self) if i == indices)  # takes time proportional to the index but constant space
    else:
        # the double reversing is needed if the step is negative
        reverse = -1 if index.step < 0 else 1  # constant time
        return [value for i,value in enumerate(self) if i in indices[::reverse]][::reverse]  # O(n) time and O(len(indices)) space

This is efficient, because testing if an int is in a range and slicing a range takes constant time.

I have implemented a recursive function to solve this problem with an extra functional parameter which will show the position of current cursor to save time in over and over start tracing from first node each and every time when recursion called.

I am not saying that other answers are wrong but I used to implement principle of KISS in my code and other answers were hard to understand.

My edited code for __getitem__() is below:

class LinkedList(Node):
    ...

    def __getitem__(self,index,fromNode=None):
        global i,temp
        if fromNode is None:
            i,temp = 0,self.__head

        if isinstance(index, int):
            if index<0: index+=len(self) 
            while temp!=None:
                if i == index:
                    return temp.value
                temp = temp.ref
                i += 1

        elif isinstance(index, slice):
            step = 1    if index.step is None else index.step
            start = 0   if index.start is None else index.start
            stop=len(self) if index.stop is None else index.stop

            if start < 0:   start += len(self)
            elif step> 0 >stop: stop += len(self)

            reverseFlag = True if step<0 else False

            if reverseFlag:
                trace = iter(reversed(range(len(self))[start:stop:step]))
            else:
                trace = iter(range(len(self))[start:stop:step])

            returningList = [self.__getitem__(indice,temp) for indice in trace]

            return list(reversed(returningList)) if reverseFlag else returningList

    ...

Can I further reduce this code or can reduce more complexity from it? If yes then please suggest me via your answer or comments.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!