1. 双向循环链表是什么
在学习数据结构中,我们常常会接触到各种各样的链表,其中就包括双向循环链表。那么什么是双向循环链表呢?
双向循环链表是一种链表结构,每个节点既有指向前驱节点的指针,也有指向后继节点的指针,链表的最后一个节点指向第一个节点,形成一个循环的链表结构。
与单向链表相比,双向循环链表的节点可以很方便地实现双向遍历,也支持在节点的前、后插入操作。这使得双向循环链表在某些场合下比单向链表更加便利。
2. 双向循环链表的实现
2.1 定义节点
在Python中实现双向循环链表,我们首先需要定义链表的节点,节点需要存储数据和指向前驱、后继节点的指针。根据定义,我们可以写出以下代码:
class Node:
def __init__(self, data):
self.data = data
self.prev = None
self.next = None
这里使用类定义节点。在节点的构造函数中,我们传入参数data表示存储的数据。同时设置prev和next为None,初始时这个节点没有前驱、后继。这个类中没有其他的方法,因为节点本身并不需要进行过多的操作,主要是用来存储数据和指向前驱、后继节点。
2.2 定义链表
定义了节点之后,我们需要定义链表。链表需要维护头节点和尾节点,并提供一些方法实现节点的插入、删除、查找等操作。以下是简单的链表定义:
class DoubleLinkedList:
def __init__(self):
self.head = None
self.tail = None
self.size = 0
在链表的构造函数中,我们初始化了头节点和尾节点,并将链表的大小size设为了0。
2.3 节点的插入操作
实现了链表的定义之后,我们需要添加节点的插入操作。节点的插入可以分为在头部插入、尾部插入和在指定位置插入几种情形,这里我们只给出在头部插入和尾部插入的操作。以下是实现节点尾部插入的代码:
class DoubleLinkedList:
def append(self, data):
node = Node(data)
if self.head is None: # 如果链表是空的,头尾节点都是新增节点
self.head = node
self.tail = node
else:
node.prev = self.tail # 否则将新增节点的prev指向当前尾节点
self.tail.next = node # 当前尾节点的next指向新增节点
self.tail = node # 更新尾节点
self.size += 1
在方法append中,我们首先创建一个新节点,然后判断链表是否为空。如果是空链表,直接将头尾节点都设置成新增节点即可。否则,将新增节点的prev指向当前尾节点,将当前尾节点的next指向新增节点,最后更新尾节点。操作完成后,链表的大小加1。
以下是实现在头部插入节点的代码:
class DoubleLinkedList:
def add_first(self, data):
node = Node(data)
if self.head is None: # 如果链表是空的,头尾节点都是新增节点
self.head = node
self.tail = node
else:
node.next = self.head # 否则将新增节点的next指向当前头节点
self.head.prev = node # 当前头节点的prev指向新增节点
self.head = node # 更新头节点
self.size += 1
代码中的实现其实和在尾部插入节点的代码非常类似。我们只需要将新增节点的next指向当前头节点,将当前头节点的prev指向新增节点,最后更新头节点。同样,操作完成后链表的大小加1。
2.4 节点的删除操作
链表的节点删除操作比节点的插入操作复杂。节点的删除需要保证节点的前驱、后继节点之间的指针正确连接,这里我们只演示如何在节点尾部删除节点。
class DoubleLinkedList:
def remove_last(self):
if self.tail is None:
raise Exception('List is empty!') # 链表为空,抛出异常
data = self.tail.data
if self.head == self.tail: # 链表只有一个节点
self.head = None
self.tail = None
else:
self.tail = self.tail.prev # 将当前尾节点的前驱节点设置为新的尾节点
self.tail.next = None # 新的尾节点的next指向None
self.size -= 1
return data
如果链表为空,这个方法会抛出异常;否则,删除尾节点需要取出尾节点中的数据。如果链表只有一个节点,将头尾节点都设置为None即可。否则,将当前尾节点的前驱节点设置为新的尾节点,新的尾节点的next指向None即可。这里的操作比较简单,需要注意的是链表中节点个数需要减1。
3. 双向循环链表的使用
实现了双向循环链表之后,我们可以进行如下的操作:
linked_list = DoubleLinkedList()
linked_list.append(1) # 将1节点添加到链表尾部
linked_list.add_first(2) # 将2节点添加到链表头部
linked_list.append(3) # 将3节点添加到链表尾部
linked_list.remove_last() # 删除链表尾部节点
通过调用此代码中定义的append、add_first、remove_last方法,我们实现了节点在链表尾部、头部的插入和尾部节点的删除操作。
除此之外,我们还可以利用定义的节点、链表进行一些常见的操作,如打印链表、链表元素的查找等。以下是实现连续输出链表元素的代码:
class DoubleLinkedList:
# ...
def __repr__(self):
nodes = []
current_node = self.head
while current_node is not None:
nodes.append(str(current_node.data))
current_node = current_node.next
if current_node == self.head:
break
return " -> ".join(nodes)
代码中定义了类的__repr__方法,用来打印整个链表的内容。首先定义了一个列表nodes,然后从头节点开始遍历链表,依次将每个节点的data属性加入到列表中。这里需要注意的是,如果循环到头了,要及时break掉循环,否则会陷入死循环。最后将nodes列表中的各项用" -> "连接起来即可。使用此方法,我们就可以通过调用print(linked_list)输出整个链表了。
4. 总结
在Python中实现双向循环链表,需要定义Node和DoubleLinkedList两个类,分别代表链表中的节点和链表本身。节点需要存储数据和指向前驱、后继节点的指针,链表本身需要维护头节点和尾节点,并提供插入、删除、查找等常见的链表操作,例如在尾部、头部插入节点,删除尾部节点等。在实际使用中,可以通过不断地插入、删除节点来实现链表的功能。