python实现数据结构中双向循环链表操作的示例

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两个类,分别代表链表中的节点和链表本身。节点需要存储数据和指向前驱、后继节点的指针,链表本身需要维护头节点和尾节点,并提供插入、删除、查找等常见的链表操作,例如在尾部、头部插入节点,删除尾部节点等。在实际使用中,可以通过不断地插入、删除节点来实现链表的功能。

后端开发标签