双向链表

Posted keithtt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了双向链表相关的知识,希望对你有一定的参考价值。

链表是实现了数据之间保持逻辑顺序,但存储空间不连续的数据结构。
每一个结点有两个域,左边部份叫值域,用于存放用户数据。右边叫指针域,存储着指向后面一个节点的指针。
head节点永远指向第一个节点。
tail节点永远指向最后一个节点。
相对于单向链表,双向链表多了一个指向前面一个节点的指针域。
链表查询效率较慢,因为查询的时候需要移动指针一个一个找。
双向链表新增和删除元素效率较高,因为链表会记录前一个节点和后一个节点。

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

    def __str__(self):
        return ‘<prev: {} <- node: {} -> next: {}>‘.format(self.prev.item if self.prev else None, self.item, self.next.item if self.next else None)

    def __repr__(self):
        return str(self.item)

class LinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.size = 0

    def append(self, item):
        node = Node(item)
        # 如果head节点为空,表示链表为空
        if self.head is None:
            self.head = node
            self.tail = node
        else:
            self.tail.next = node
            node.prev = self.tail
            self.tail = node
        self.size += 1
        return self

    def index(self, item):
        for i, node in enumerate(self):
            if item == node.item:
                return i
        else:
            raise ValueError(‘item does not exist‘)

    def insert(self, index, item):
        if index < 0:
            raise IndexError

        # 如果找到就将current指向对应位置的节点,继续后面的操作
        # 如果没有找到(链表为空或者索引超界),则追加并立即返回,后面的操作将不会执行
        for i, node in enumerate(self):
            if i == index:
                current = node
                break
        else:
            self.append(item)
            return

        node = Node(item)
        prev = current.prev

        # 从头部插入
        if prev is None:
            node.next = current
            current.prev = node
            self.head = node
        # 从中间插入(尾部插入即追加,上面已经实现)
        else:
            node.next = current
            node.prev = prev
            prev.next = node
            current.prev = node

        self.size += 1

    def pop(self):
        # 如果尾部为空,则表示链表为空
        if self.tail is None:
            raise Exception(‘empty‘)

        node = self.tail
        item = node.item
        prev = node.prev

        # 如果尾部节点没有前节点,则表示链表只有一个元素
        if prev is None:
            self.head = None
            self.tail = None
        # 剩下就是有多个节点的情况
        else:
            prev.next = None
            self.tail = prev

        self.size -= 1
        return item

    def remove(self, index):
        if index < 0:
            raise IndexError(‘does not support negative index‘)

        # 如果链表为空,则抛出异常
        if self.head is None:
            raise Exception(‘empty‘)

        # 如果找到就将current指向对应位置的节点,继续后面的操作
        # 如果没有找到(链表为空或者索引超界),则抛出异常
        for i, node in enumerate(self):
            if i == index:
                current = node
                break
        else:
            raise IndexError(‘index out of range‘)

        prev = current.prev
        next = current.next

        # 如果current没有前节点也没有后节点,表示只有一个节点
        if prev is None and next is None:
            self.head = None
            self.tail = None
        # 如果不止一个节点,且如果current没有前节点,表示current是head
        elif prev is None:
            next.prev = None
            self.head = next
        # 如果不止一个节点、current不是head,且如果current没有后节点,表示current是tail
        elif next is None:
            prev.next = None
            self.tail = prev
        # 剩下就是多节点从中间remove的情况
        else:
            prev.next = next
            next.prev = prev

        self.size -= 1

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

    def clear(self):
        for node in self:
            node.prev = None
            node.tail = None
        self.head = None
        self.tail = None
        self.size = 0

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

    def __reversed__(self):
        current = self.tail
        while current:
            yield current
            current = current.prev

    def __len__(self):
        # current = self.head
        # count = 0
        # while current:
        #     count += 1
        #     current = current.next
        # return count
        return self.size

    def __getitem__(self, key):
        # 支持负索引
        obj = reversed(self) if key < 0 else self
        start = 1 if key < 0 else 0

        for i, node in enumerate(obj, start):
            if abs(key) == i:
                return node
        else:
            raise IndexError(‘index out of range‘)

    def __setitem__(self, key, value):
        # for i, node in enumerate(self):
        #     if key == i:
        #         node.item = value
        # else:
        #     self.append(value)
        self[key].item = value


linklist = LinkedList()

# 测试append()
for i in range(10):
    linklist.append(i)

# 测试__iter__
for node in linklist:
    print(node)

# 测试__reversed__
for node in reversed(linklist):
    print(node)

# 测试__len__
print(len(linklist))

# 测试index()
print(linklist.index(3))

# 测试insert()
linklist.insert(0, 0)
print(list(linklist))

# 测试pop()
print(linklist.pop())

# 测试__getitem__
print(linklist[3])
# print(linklist[13])
print(linklist[-1])

# 测试__setitem__
linklist[5] = 15
print(list(linklist))

# 测试clear()
linklist.clear()
print(list(linklist))
print(len(linklist))

参考:
http://zhaochj.github.io/2016/05/12/2016-05-12-数据结构-链表/
https://blog.csdn.net/qq_26118603/article/details/79039738
http://blog.suchasplus.com/2011/03/why-python-Standard-library-does-not-implement-the-list.html
https://www.zhihu.com/question/55721190











以上是关于双向链表的主要内容,如果未能解决你的问题,请参考以下文章

带头节点的双向链表

《链表》之带头双向循环链表

7L-双线链表实现

代码模板实现双向链表的去重拼接合并排序

数据结构之带头结点的循环双向链表详细图片+文字讲解

双向链表 - 是啥导致我的代码引发编译器错误,我该如何解决?