python3循环链表检测、返回首节点_【数据结构与算法Python描述】——单向循环链表简介、Python语言实现及应用...

【数据结构与算法Python描述】——单向线性链表简介及其Python实现给出了链表的最简单形式——单向线性链表,以及其Python语言实现和相关应用。

实际上,基于单向线性链表的变形很多,而单向循环链表(本文简称“循环链表”)是其中一种较为常见的形式。

一、单向循环链表引入

在文章【数据结构与算法Python描述】——队列和双端队列简介及其高效率版本Python实现中,我们首次引入了循环的概念,但实际上列表的内存模型并不存在任何首尾相连的特征,只是通过使用取模运算可以使得类似循环的特点。

对于链表,特别是单向线性链表,通过对其进行一定的变形可以获得真正意义上的环状形态,即如下图所示,让原本引用None的单向线性链表尾结点的next域引用头结点。

实际上,对于循环链表,已经不存在所谓头和尾的概念了,所以一般地可将循环链表画成如下图所述形式。对于这一说法以及循环链表的模型类比理解,一个现实中的例子是上海的地铁4号线,这是一条以横贯方式连接上海所有主要地铁线的环形线。

尽管说循环链表不存在开始和结束的概念,还是有必要定义一个变量使其引用循环链表中的某一个结点,上图使用名为current的变量,只有这样才能通过类似current = current.next的语法遍历找到链表中的所有节点。

二、单向循环链表应用

对于循环链表,我们不会像单向线性链表一样去实现其ADT的所有方法,因为循环不存在所谓头(尾)部插入(删除)等概念,即使实现了也大同小异。更多地,我们将循环链表的一些特性来实现特定的功能,比如下面的循环调度算法。

1. 循环调度算法

循环调度算法可以实现这样一种功能:对于某一个对象集合,该算法可以一种循环的方式挨个获取对象中的每个元素,然后对该元素做某种处理。

典型地使用循环调度算法的案例是计算机CPU为电脑上的不同应用分配执行时间片,我们知道计算机上的CPU数量一般远小于正在运行的应用数量,计算机就是利用诸如循环调度算法实现CPU在不同应用之间轮流切换执行,从而达到看似多个应用同时执行的效果。

2. 循环链表实现队列

2.1 分析

对于循环调度算法的实现,可以使用一个普通的队列来实现,即循环执行下列三个步骤:

使用e = queue.dequeue()获取应用e;

为元素e代表的应用服务;

使用queue.enqueue(e)将已被服务的元素e重新入队。

实际上,如果使用【数据结构与算法Python描述】——队列和双端队列简介及其高效率版本Python实现中的ListQueue来实现循环调度算法,则在每次循环中都需要先对某一元素执行一次队头出队操作,再对同一个元素执行一次队尾入队操作。

如果使用循环链表的思想,那么头结点出队然后在尾结点处入队的动作只需一个方法(一般名为rotate())即可实现,如下图所示:

先将队列的头尾结点链接在一起;

然后在该方法中使用标记当前头和尾结点的变量,使得在一次循环中将当前头结点变为尾结点,当前头结点的下一个结点成为新的头结点。

2.2 ADT

由上述分析,如果将使用循环链表思想实现支持循环调度算法的队列命名为CircularQueue,则其ADT至少包含下列方法:

方法名称

方法描述

__len__()

重写该方法,使对于CircularQueue的对象可以使用len()方法

is_empty()

判断CircularQueue对象是否为空

first()

返回但并不删除队列的头部元素

rotate()

完成头部元素出队并从尾部入队操作

enqueue(e)

向当前队列的尾部加入新的元素

dequeue()

删除当前队列中在队头的元素

2.3 实现

为了实现上述ADT包含的方法,乍一看似乎两个指向结点的变量,即self._head和self._tail。实际上,使用一个self._tail即可,因为总是可以通过self._tail.next来获取头结点的引用。

因此,使用下列两个变量即可实现CircularQueue类:

self._tail:指向尾结点的变量;

self._size:保存当前队列中元素数量的变量。

class Empty(Exception):

"""尝试对空队列进行删除操作时抛出的异常"""

pass

class _Node:

"""节点类"""

def __init__(self, element, next=None):

"""

:param element: 节点代表的对象元素

:param next: 下一个节点

"""

self.element = element

self.next = next

class CircularQueue:

"""支持循环调度算法的队列"""

def __init__(self):

"""创建一个空的队列"""

self._tail = None

self._size = 0

def __len__(self):

"""

返回队列中元素个数

:return: 队列中元素个数

"""

return self._size

def is_empty(self):

"""

如果队列为空则返回True,否则返回False

:return: 队列是否为空的标识

"""

return self._size == 0

def first(self):

"""

返回但不删除队头结点的元素

:return: 队头结点的元素

"""

if self.is_empty():

raise Empty('当前队列为空!')

head = self._tail

return head.element

def rotate(self):

"""

完成头部元素出队并从尾部入队操作

:return: None

"""

if self._size > 0:

self._tail = self._tail.next # 标识尾结点的变量指向当前头结点

def enqueue(self, element):

"""

向当前队列的尾部加入新的对象元素

:param element: 待从尾部入队的对象元素

:return: None

"""

node = _Node(element) # 元素封装成结点

if self.is_empty():

self._tail = node

self._tail.next = node # 链表成环

node.next = self._tail.next # 新结点和当前头结点建链

self._tail.next = node # 当前尾结点和新结点建链

self._tail = node # 新结点成为尾结点

self._size += 1

def dequeue(self):

"""

删除当前队列中在队头的元素

:return: 队头的元素

"""

if self.is_empty():

raise Empty('当前队列为空!')

old_head = self._tail.next

if self._size == 1:

self._tail = None # 将队列置为空

else:

self._tail.next = old_head.next # 直接跳过旧的头结点

self._size -= 1

return old_head.element

对于上述代码,有两点值得记录的是:

对于rotate()方法,在Python的collections模块中,其deque类有个功能类似的同名方法;

对于enqueue(e)方法,由于出现了两次完全一样的语句self._tail = node,其代码可以修改成如下形式:

def enqueue(self, element):

"""

向当前队列的尾部加入新的对象元素

:param element: 待从尾部入队的对象元素

:return: None

"""

node = _Node(element) # 元素封装成结点

if self.is_empty():

node.next = node # 链表成环

node.next = self._tail.next # 新结点和当前头结点建链

self._tail.next = node # 当前尾结点和新结点建链

self._tail = node # 新结点成为尾结点

self._size += 1

你可能感兴趣的:(python3循环链表检测,返回首节点)