首页 > 编程语言 >Python: DoubleLinkedList

Python: DoubleLinkedList

时间:2022-11-10 13:00:09浏览次数:44  
标签:node head Python self value DoubleLinkedList tail prev

 

import typing


class Node:
    def __init__(self, value):
        self.value = value
        self.prev = None
        self.next = None

    def __repr__(self):
        return f'Node({self.value})'

    def __eq__(self, other):
        if isinstance(other, Node):
            return self.value == other.value
        return False


class DoubleLinkedList:
    def __init__(self):
        self.head: Node = None
        self.tail: Node = None

    def append(self, value):
        node = Node(value)
        if self.head is None:
            self.head = node
        else:
            self.tail.next = node
            node.prev = self.tail
        self.tail = node

    def iter(self, reverse=False):
        current = self.head if not reverse else self.tail
        while current:
            yield current
            current = current.next if not reverse else current.prev

    def __iter__(self):
        current = self.head
        while current:
            yield current
            current = current.next

    def pop(self, index: int = None):
        if self.head is None:
            raise IndexError('pop from empty list')
        length = len(self)  # > 0
        if index is None:
            index = -1
        if index < 0:
            if index < -length:
                raise IndexError('pop index out of range')
            if length == 1:  # only have one node, node.prev == node.next == None, no need to care about ref
                node = self.head
                self.head = self.tail = None
                return node.value
            # below code situation: have more than one node
            if index == -1:  # the last node
                tail = self.tail
                tail.prev.next = None
                self.tail = tail.prev
                tail.prev = None  # remove ref
                return tail.value
            elif index == -length:  # the first node
                head = self.head
                self.head = head.next
                self.head.prev = None
                head.next = None
                return head.value
            else:  # node in the middle position
                node = self[index]  # we support negative index
                prev = node.prev
                next = node.next
                prev.next = next
                next.prev = prev
                node.prev = node.next = None
                return node.value
        else:
            if index > length - 1:
                raise IndexError('pop index out of range')
            if length == 1:
                node = self.head
                self.head = self.tail = None
                return node.value
            # below code situation: have more than one node
            if index == 0:  # the first node
                head = self.head
                self.head = head.next
                self.head.prev = None
                head.next = None
                return head.value
            elif index == length - 1:  # the last node
                tail = self.tail
                self.tail = tail.prev
                self.tail.next = None
                tail.prev = None
                return tail.value
            else:  # node in the middle position
                node = self[index]
                prev = node.prev
                next = node.next
                prev.next = next
                next.prev = prev
                node.prev = node.next = None
                return node.value

    # __iter__ = iter

    def __getitem__(self, item):
        # for i, node in enumerate(self.iter(False if i > 0 else True), start=0 if i > 0 else 1):
        #     if i == abs(item):
        #         return node.value
        if not isinstance(item, int | slice):
            raise TypeError(f'LinkedList indices must be integers or slices, not{type(item)}')  # sequence
        if isinstance(item, int):
            if item < 0:  # index = -i - 1
                for i, v in enumerate(self.iter(reverse=True)):
                    if item == -i - 1:
                        return v
                raise IndexError('Index Out of Bounds')  # for loops detect the end of sequence
            else:
                for i, v in enumerate(self.iter()):
                    if item == i:
                        return v
                raise IndexError('Index Out of Bounds')  # for loops detect the end of sequence
        else:  # item is slice object
            dll = self.__class__()  # construct a new instance
            item: slice
            indices = list(range(*item.indices(len(self))))
            print('indices', indices, len(self))
            # step > 0 indices [2, 4, 6]
            # step < 0 indices [6, 4, 2]
            for i in indices:
                dll.append(self[i].value)
            return dll
            # if item.start > item.stop:  # [3,2,1]  step<0
            #     for i,v in enumerate
            # else:  # step>0
            #     for i, v in enumerate(self, start=0):
            #         if i in indices:
            #             dll.append(v.value)  # append need value, not node
            #     return dll

    def __setitem__(self, key, value):
        if not isinstance(key, int | slice):
            raise TypeError(f'LinkedList indices must be integers or slices, not{type(key)}')  # sequence

        if isinstance(key, int):
            self[key] = Node(value)

        if isinstance(key, slice):
            if not isinstance(value, typing.Iterable):  # slice require value to be an iterable, even if slice has one item
                raise TypeError('can only assign an iterable')

            if key.step == 0:
                raise ValueError('slice step cannot be zero')  # slice does not judge step

            length = len(self)

            if not length:
                for v in value:
                    self.append(v)

            indices = list(range(*key.indices(len(self))))
            start = key.start
            stop = key.stop
            step = key.step

            step = 1 if step is None else step  # step default to 1

            if step > 0:
                if start is None:
                    start = 0
                if stop is None:
                    stop = length
            else:
                if start is None:
                    start = -1
                if stop is None:
                    stop = -length - 1

            if not len(indices):  # slice is null, turn into insert behavior, start position is key.start
                if start == 0 or start == -length:  # the first node
                    flag = False
                    _tail = None  # temp tail node
                    for v in value:
                        node = Node(v)
                        if not flag:
                            head = self.head
                            self.head = node
                            flag = True
                            _tail = node  # store temp tail node
                        else:
                            _tail.next = node
                            node.prev = _tail
                            _tail = node
                    else:  # concatenate self.tail and head
                        if _tail is None:  # len(value) == 0
                            return
                        _tail.next = head
                        head.prev = _tail
                elif start >= length:  # append the value
                    for v in value:
                        self.append(Node(v))
                else:  # middle position
                    _head = self[start]
                    _tail = _head.prev
                    for v in value:
                        node = Node(v)
                        _tail.next = node
                        node.prev = _tail
                        _tail = node
                    _tail.next = _head
                    _head.prev = _tail

            if step > 0:
                print('__setitem__', key, indices, start, stop, step)

                if step == 1:  # adjacent, no need to care about numbers, -1 is not this case
                    if start == 0:  # the first node
                        if stop >= length:  # override the whole LinkedList
                            for v in self:
                                v.prev = v.next = None
                            self.head = self.tail = None
                            for v in value:
                                node = Node(v)
                                if self.head is None:
                                    self.head = node
                                    self.tail = node
                                else:
                                    self.append(node)

            if step > 1:  # not adjacent, require len(range(*key.indices(len(self)))) == len(value)
                ...

    def __len__(self):
        flag = False
        for i, _ in enumerate(self.iter()):
            flag = True
        if flag:
            return i + 1
        return 0

    def __reversed__(self):  # it should return a new iterator object in reverse order, fall back to sequence __len__ __getitem__
        print('__reversed__')
        for i in range(len(self) - 1, -1, -1):
            yield self[i]

    def remove(self, value, reverse=False):  # enhancement, because we are DoubleLinkedList
        length = len(self)
        if not length:
            raise ValueError('empty!')
        node = Node(value)
        flag = False

        try:
            if reverse:
                for i, v in enumerate(self.iter(reverse=True), start=0):
                    if node == v:  # found
                        self.pop(-i - 1)  # convert to negative index
                        break
                else:
                    raise IndexError
            else:
                for i, v in enumerate(self.iter()):
                    if node == v:
                        self.pop(i)
                        break
                else:
                    raise IndexError
        except IndexError:
            raise ValueError(f'{value} not in list') from None

    def insert(self, index, value):
        length = len(self)
        node = Node(value)
        if length == 0:
            self.head = self.tail = node
        else:
            if index < 0:
                if index <= -length:  # the first position
                    head = self.head
                    self.head = node
                    node.next = head
                    head.prev = node
                else:
                    for i, v in enumerate(self.iter(reverse=True)):
                        if index == -i - 1:
                            prev = v.prev
                            prev.next = node
                            node.prev = prev
                            node.next = v
                            v.prev = node
            else:
                if index == 0:  # the first position
                    head = self.head
                    self.head = node
                    node.next = head
                    head.prev = node
                elif index >= length:  # the last position
                    tail = self.tail
                    self.tail = node
                    tail.next = node
                    node.prev = tail
                else:  # the middle position
                    for i, v in enumerate(self.iter()):
                        if index == i:
                            prev = v.prev
                            prev.next = node
                            node.prev = prev
                            node.next = v
                            v.prev = node

    def __repr__(self):
        # print(11, list(map(str, self)))
        # return ', '.join(map(str, self))
        # print(22, list(self))
        return f'[{", ".join(list(map(str, self)))}]'


l = DoubleLinkedList()
l.append(3)
l.append(4)
l.append(5)
l.append(6)
l.append(7)
l.append(8)
# l[0:0] = 'ab'
# l[-len(l):0] = 'AB'
l[3:3] = ''
print(l)
# dd = l[2:88:2]
dd = l[-2:-20:-2]
print('dd =', type(dd), dd)
print('l =', l)
print(list(reversed(l)))
# print(l.pop(-2))
# print(l.pop())
# l.remove(4, reverse=True)
# print(l.pop())
# l.insert(-10, 'a')
# l.insert(-len(l), 'aa')
# l.insert(-1, 'bb')
# l.insert(len(l) - 1, 'bb')
for n in l.iter():
    print(n, type(n))

# for n in l.iter(reverse=True):
#     print(n, type(n))

# print(l[0], l[-1], l[3], l[-4])

# print(l[8])
# print(l[-5])
print(len(l))

ll = DoubleLinkedList()
print(len(ll))
print(ll.__module__)

 

标签:node,head,Python,self,value,DoubleLinkedList,tail,prev
From: https://www.cnblogs.com/dissipate/p/16876713.html

相关文章