iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Python数据结构之循环链表详解
  • 399
分享到

Python数据结构之循环链表详解

2024-04-02 19:04:59 399人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录0. 学习目标1. 循环链表简介2. 循环单链表实现2.1 循环单链表的基本操作2.2 简单的实现方法2.3 循环单链表应用示例2.4 利用循环单链表基本操作实现复杂操作3. 循

0. 学习目标

循环链表 (Circular Linked List) 是链式存储结构的另一种形式,它将链表中最后一个结点的指针指向链表的头结点,使整个链表头尾相接形成一个环形,使链表的操作更加方便灵活。我们已经介绍了单链表和双向链表,本节中我们将基于单链表和双向链表实现循环链表与循环双链表以及它们的相关操作。
通过本节学习,应掌握以下内容:

循环链表的基本概念及其优缺点

循环链表及其操作的实现

循环双链表及其操作的实现

1. 循环链表简介

在单链表/双向链表中,最后一个结点的指针域为 None,访问链表中任何数据只能从链表头开始顺序访问,而不能进行任何位置的随机查询访问,如果查询的结点在链表的尾部也需遍历整个链表。

循环链表是一种特殊的链表,在循环链表中,首尾端点相互连接,使整个链表头尾相接形成一个环形,也就是说链表中的最后一个结点指向第一个结点;换句话说,在循环链表中所有结点都指向下一个结点,每一个结点都有一个后继结点。

循环链表的优点是,从链表中任一结点出发,顺着指针链都可找到表中其它结点,因此没有结点指向 None;同时并不会占用额外的存储空间,仅仅是改变了最后一个结点的指针指向,就可以使操作更加方便灵活。

循环链表可以基于单链表和双向链表,通常基于单链表的循环链表称为循环单链表,而基于双向链表的循环链表称为循环双链表,两种类型的循环链表示意图如下所示:

NOTE:由于我们对于链表已经非常熟悉了,因此对于链表中相似的操作只会进行简要的介绍,我们的主要精力将放在具有差异的操作上。

2. 循环单链表实现

类似于单链表,接下来让我们实现一个带有头结点的循环单链表类,并用头指针标识链表的开头,如果你还不了解单链表,可以参考《单链表及其操作实现》相关介绍。

2.1 循环单链表的基本操作

循环单链表的基本操作大部分与单链表相同,只是循环遍历是否完成的条件需要由 current == None 改为 current != head

2.1.1 循环单链表的初始化

初始化循环单链表函数中,创建的头结点指针域 next 不为空,而是指向自身:

class CircularLinkedList:
    def __init__(self):
        head_node = Node()
        self.head = head_node
        head_node.next = head_node
        self.length = 0

2.1.2 获取循环单链表长度

重载 __len__ 从对象返回 length 的值用于求取循环单链表长度:

    def __len__(self):
        return self.length

2.1.3 读取指定位置元素

重载 __getitem__ 操作用于实现读取链表指定位置元素的操作,类似的,我们也可以实现修改指定位置元素的操作,只需要重载 __setitem__ 操作,它们的时间复杂度均为O(n):

def __getitem__(self, index):
        if index > self.length - 1 or index < 0:
            raise IndexError("CircularLinkedList assignment index out of range")
        else:
            count = -1
            current = self.head
            while count < index:
                current = current.next
                count += 1
            return current.data
    def __setitem__(self, index, value):
        if index > self.length - 1 or index < 0:
            raise IndexError("CircularLinkedList assignment index out of range")
        else:
            count = -1
            current = self.head
            while count < index:
                current = current.next
                count += 1

            current.data = value

我们可以发现,这两个操作与单链表的操作完全相同。

2.1.4 查找指定元素

在循环单链表中查找指定元素的操作与单链表基本相同,使用 current 指针沿后继链依次遍历每个结点,并判断其值是否等于指定值 value,若是则返回该结点索引,否则继续往后搜索;只是循环遍历是否完成的条件需要由 current == None 改为 current != head:

    def locate(self, value):
        count = 0
        current = self.head.next
        while current != self.head and current.data != value:
            count += 1
            current = current.next
        if current and current.data == value:
            return count
        else:
            raise ValueError("{} is not in sequential list".fORMat(value))

2.1.5 在指定位置插入新元素

由于有 length 属性的原因,我们可通过 length 判断插入位置是否合法,因此在循环单链表中的指定位置插入新元素与在单链表指定位置插入新元素完全相同:

    def insert(self, index, data):
        count = -1
        current = self.head
        # 判断插入位置的合法性
        if index > self.length or index < 0:
            raise IndexError("CircularLinkedList assignment index out of range")
        else:
            node = Node(data)
            while count < index:
                # 查找插入位置
                previous = current
                current = current.next
                count += 1
            # 插入新结点
            node.next = previous.next
            previous.next = node
            self.length += 1

2.1.6 删除指定位置元素

要删除链表中第i个结点,同样首先找到删除位置的前一个结点 previous,指针 current 指向要删除的结点,将 previous 的指针域修改为待删除结点 current 的后继结点的地址,删除后的结点需动态的释放:

    def __delitem__(self, index):
        if index > self.length - 1 or index < 0:
            raise IndexError("CircularLinkedList assignment index out of range")
        else:
            count = -1
            previous = self.head
            while count < index - 1:
                previous = previous.next
                count += 1
            current = previous.next
            previous.next = current.next
            self.length -= 1
            del current

2.1.7 其它一些有用的操作

[1] 使用 str 函数调用对象上的 __str__ 方法可以创建适合打印的字符串表示:

     def __str__(self):
        head = self.head
        s = "["
        current = self.head.next
        count = 0
        while current != head:
            count += 1
            s += str(current)
            current = current.next
            if count < self.length:
                s += '-->'
        s += "]"
        return s

[2] 删除指定元素,其时间复杂度与删除指定位置元素相同,都为O(n):

    def del_value(self, value):
        previous = self.head
        current = self.head.next
        while current != self.head:
            if current.data == value:
                previous.next = current.next
                self.length -= 1
                del current
                return
            else:
                previous = current
                current = current.next
        raise ValueError("The value provided is not present!")

[3] 为了方便的在链表尾部追加新元素,可以实现函数 append:

    def append(self, value):
        node = Node(value)
        current = self.head.next
        while current.next != self.head:
            current = current.next
        node.next = current.next
        current.next = node
        self.length += 1

2.2 简单的实现方法

从上述实现,我们可以看到,CircularLinkedList 类的代码中大部分与 SinglyLinkedList 类完全相同,如果你对继承机制还有印象的话,我们也可以令 CircularLinkedList 继承自在《单链表及其操作实现》中实现的 SinglyLinkedList,简化循环单链表的实现。

from SinglyLinkedList import SinglyLinkedList
class CircularLinkedList(SinglyLinkedList):
    """
    利用继承机制仅需要实现循环单链表与单链表中不同的操作,仅需要重载父类中:
    __init__(), locate(), del_value(), __str__(), append() 函数即可
    相关代码在上一小节已经给出,这里不再赘述
    """
    pass

2.3 循环单链表应用示例

接下来,我们将测试上述实现的循环单链表,以验证操作的有效性。首先初始化一个链表 cllist,并在其中追加若干元素:

cllist = CircularLinkedList()
# 在循环单链表末尾追加元素
cllist.append('apple')
cllist.append('lemon')
# 在指定位置插入元素
cllist.insert(0, 'banana')
cllist.insert(2, 'orange')

我们可以直接打印链表中的数据元素、链表长度等信息:

print('循环单链表 cllist 数据为:', cllist)
print('循环单链表 cllist 长度为:', len(cllist))
print('循环单链表 cllist 第 0 个元素为:', cllist[0])
cllist[0] = 'pear'
print('修改循环单链表 cllist 后数据元素为:', cllist)

以上代码输出如下:

循环单链表 cllist 数据为: [banana-->apple-->orange-->lemon]
循环单链表 cllist 长度为: 4
循环单链表 cllist 第 0 个元素为: banana
修改循环单链表 cllist 后数据元素为: [pear-->apple-->orange-->lemon]

接下来,我们将演示在指定位置添加/删除元素、以及如何查找指定元素等:

# 在指定位置添加/删除结点
cllist.insert(1, 'grape')
print('在位置 1 添加 grape 后循环单链表 cllist 数据:', cllist)
del(cllist[2])
print('修改循环单链表 cllist 后数据:', cllist)
# 删除指定元素
cllist.del_value('pear')
print('删除 pear 后循环单链表 cllist 数据:', cllist)
cllist.append('watermelon')
print('添加 watermelon 后循环单链表 cllist 数据:', cllist)

以上代码输出如下:

在位置 1 添加 grape 后循环单链表 cllist 数据: [pear-->grape-->apple-->orange-->lemon]
修改循环单链表 cllist 后数据: [pear-->grape-->orange-->lemon]
删除 pear 后循环单链表 cllist 数据: [grape-->orange-->lemon]
添加 watermelon 后循环单链表 cllist 数据: [grape-->orange-->lemon-->watermelon]

2.4 利用循环单链表基本操作实现复杂操作

[1] 将两个循环单链表首尾相接进行合并,连接示例如下图所示:

与单链表不同,循环单链表的连接不仅需要遍历第一个链表找到最后一个结点连接到第二个链表上,还需要遍历第二个链表,使第二个链表的尾结点的 next 指针指向第一个链表的头结点,具体实现如下:

def merge(cllist1, cllist2):
    current = cllist1.head
    while current.next != cllist1.head:
        current = current.next
    # 若cllist2不为空,进行连接操作
    if cllist2.head.next != cllist2.head:
        current.next = cllist2.head.next
        current2 = cllist2.head.next
        while current2.next != cllist2.head:
            current2 = current2.next
        current2.next = cllist1.head
        cllist1.length += len(cllist2)
        return cllist1
# 算法测试
cllist1 = CircularLinkedList()
cllist2 = CircularLinkedList()
for i in range(5):
    cllist1.append(i)
    cllist2.append((i+1)*5)
print('循环单链表 cllist1:', cllist1)
print('循环单链表 cllist2:', cllist2)
result = merge(cllist1, cllist2)
print('循环链表连接结果:', result)

算法执行结果如下:

循环单链表 cllist1: [0-->1-->2-->3-->4]
循环单链表 cllist2: [5-->10-->15-->20-->25]
循环单链表连接结果: [0-->1-->2-->3-->4-->5-->10-->15-->20-->25]

3. 循环双链表实现

类似于双向链表,接下来我们实现一个带有头结点的循环双链表类,并用头指针标识链表的开头,如果你还不了解双向链表,可以参考《双向链表及其操作实现》相关介绍。

3.1 循环双链表的基本操作

由于循环双链表类 DoubleLinkedCircularList 的代码中大部分与双向链表类 DoublyLinkedList 完全相同,因此我们借助继承机制来简化循环双链表的实现,DoublyLinkedList 类的实现参考《双向链表及其操作实现》。

from DoublyLinkedList import DoublyLinkedList

class Node:
    def __init__(self, data=None):
        self.data = data
        self.next = None
        self.previous = None

    def __str__(self):
        return str(self.data)

循环双链表的初始化,不仅需要将头结点的 next 指针指向自身外,previous 指针同样需要指向自身:

class DoubleLinkedCircularList(DoublyLinkedList):
    def __init__(self, data=None):
        self.length = 0
        # 初始化头结点
        head_node = Node() 
        self.head = head_node
        self.head.next = self.head
        self.head.previous = self.head

定位元素位置,同样只需要修改遍历完成条件即可:

    def locate(self, value):
        count = 0
        current = self.head.next
        while current != self.head and current.data != value:
            count += 1
            current = current.next
        if current and current.data == value:
            return count
        else:
            raise ValueError("{} is not in sequential list".format(value))

相比于双链表,循环双链表的删除和插入操作更加方便,由于其循环特性的原因,并不需要考虑所删除或插入的结点是否是链表中的最后一个结点:

    def __delitem__(self, index):
        # 删除指定位置结点
        if index > self.length - 1 or index < 0:
            raise IndexError("DoubleLinkedCircularList assignment index out of range")
        else:
            count = -1
            current = self.head
            while count < index:
                current = current.next
                count += 1
            current.previous.next = current.next
            current.next.previous = current.previous
            self.length -= 1
            del current
    
    def del_value(self, value):
        # 删除链表中的指定元素
        current = self.head
        while current.next != self.head:
            if current.data == value:
                current.previous.next = current.next
                current.next.preious = current.previous
                self.length -= 1
                del current
                return
            else:
                current = current.next
        raise ValueError("The value provided is not present!")
    
    def insert(self, index, data):
        count = 0
        current = self.head
        # 判断插入位置的合法性
        if index > self.length or index < 0:
            raise IndexError("DoubleLinkedCircularList assignment index out of range")
        else:
            new_node = Node(data)
            while count < index:
                current = current.next
                count += 1
            new_node.next = current.next
            current.next.previous = new_node
            new_node.previous = current
            current.next = new_node
            self.length += 1

由于继承的缘故,并不需要在子类中重写相同的操作(类如查找指定元素等),最后我们重载一些其它有用的操作: 

  def append(self, data):
        new_node = Node(data)
        current = self.head
        while current.next != self.head:
            current = current.next
        new_node.next = current.next
        current.next.previous = new_node
        new_node.previous = current
        current.next = new_node
        self.length += 1

    def __str__(self):
        head = self.head
        s = "["
        current = self.head.next
        count = 0
        while current != head:
            count += 1
            s += str(current)
            current = current.next
            if count < self.length:
                s += '<-->'
        s += "]"
        return s

3.2 循环双链表应用示例

接下来,我们测试上述实现的循环双链表,以验证操作的有效性:

dlclist = DoubleLinkedCircularList()
# 在链表末尾追加元素
dlclist.append('apple')
dlclist.append('lemon')
# 在指定位置插入元素
dlclist.insert(0, 'banana')
dlclist.insert(2, 'orange')
print('循环双链表 dlclist 数据为:', dlclist)
print('循环双链表 dlclist 长度为:', len(dlclist))
# 查找指定元素,这里就是调用父类的locate方法
print('apple 在 dlclist 中序号为:', dlclist.locate('apple'))
# 获取指定位置元素
print('循环双链表 dlclist 第 0 个元素为:', dlclist[0])
# 修改数据元素
dlclist[0] = 'pear'
print('修改循环双链表 dlclist 后数据元素为:', dlclist)
del(dlclist[2])
print('修改循环双链表 dlclist 后数据:', dlclist)
# 删除指定元素
dlclist.del_value('pear')
print('删除 pear 后循环双链表 dlclist 数据:', dlclist)

上述程序输出如下所示:

循环双链表 dlclist 数据为: [banana<-->apple<-->orange<-->lemon]
循环双链表 dlclist 长度为: 4
apple 在 dlclist 中序号为: 1
循环双链表 dlclist 第 0 个元素为: banana
修改循环双链表 dlclist 后数据元素为: [pear<-->apple<-->orange<-->lemon]
修改循环双链表 dlclist 后数据: [pear<-->apple<-->lemon]
删除 pear 后循环双链表 dlclist 数据: [apple<-->lemon]

以上就是python数据结构之循环链表详解的详细内容,更多关于Python循环链表的资料请关注编程网其它相关文章!

--结束END--

本文标题: Python数据结构之循环链表详解

本文链接: https://www.lsjlt.com/news/163598.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Python数据结构之循环链表详解
    目录0. 学习目标1. 循环链表简介2. 循环单链表实现2.1 循环单链表的基本操作2.2 简单的实现方法2.3 循环单链表应用示例2.4 利用循环单链表基本操作实现复杂操作3. 循...
    99+
    2022-11-13
  • C语言数据结构之双链表&循环链表&静态链表详解
    目录单链表 VS 双链表双链表双链表的初始化(带头结点)双链表的插入双链表的删除双链表的遍历循环单链表循环双链表循环双链表的初始化循环双链表的插入循环双链表的删除静态链表什么是静态链...
    99+
    2022-11-13
  • Python数据结构之链表详解
    目录0.学习目标1.线性表的链式存储结构1.1指针相关概念1.2指针结构1.3结点1.4结点类2.单链表的实现2.1单链表的初始化2.2获取单链表长度2.3读取指定位置元素2.4查找...
    99+
    2022-11-12
  • Python数据结构之双向链表详解
    目录0. 学习目标1. 双向链表简介1.1 双向链表介绍1.2 双向链表结点类1.3 双向链表优缺点2. 双向链表实现2.1 双向链表的初始化2.2 获取双向链表长度2.3 读取指定...
    99+
    2022-11-13
  • Java数据结构之链表详解
    目录一、链表的介绍二、单链表的实现三、双向链表的实现四、循环链表的实现五,链表相关的面试题一、链表的介绍 什么是链表 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑...
    99+
    2022-11-12
  • C++数据结构之链表详解
    目录前言一、删除链表中给定值为key的节点二、反转链表三、返回链表的中间节点四、删除链表的倒数第K个节点五、分割链表六、合并两个有序链表七、删除有序链表中重复节点八、环形链表九、相交...
    99+
    2022-11-12
  • Redis数据结构之链表详解
    目录1 链表和链表节点的结构2 链表相关的API1 链表和链表节点的结构 1.1 节点结构 节点的结构大概长下边这个样子: 那么,把这些节点就连起来就成了这个样子: 1.2 链表...
    99+
    2022-11-12
  • Python数据结构与算法之链表,无序链表详解
    目录我们首先来构造节点。节点(Node)的类构建完毕后,接下来我们开始构建整个链表(LinkList)的类。那么我们还需要一个方法来判断链表头的指向。接下来我们构建链表节点的添加方法...
    99+
    2022-11-13
  • Java数据结构与算法学习之循环链表
    目录存储结构示意图初始化循环链表 循环链表的插入首位置代码实现其他位置代码实现(总)循环链表的删除1.操作的为第一个元素2.操作元素不为第一个元素代码实现(总)循环链表的常见操作  ...
    99+
    2022-11-12
  • JavaScript数据结构之单链表和循环链表的示例分析
    这篇文章将为大家详细讲解有关JavaScript数据结构之单链表和循环链表的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。链表是一种物理存储单元上非线性、非连续性...
    99+
    2022-10-19
  • Java数据结构之单链表详解
    目录一、图示二、链表的概念及结构 三、单链表的实现四、完整代码的展示 一、图示 二、链表的概念及结构 链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的...
    99+
    2022-11-12
  • python数据结构之链表
    '''' 链表的实现,单向链表 ''' '''建立节点''' class jd:     def __init__(self,data):         self.data = data         self.next = None...
    99+
    2023-01-31
    数据结构 链表 python
  • Python循环结构详解
    目录一、内容简介二、while循环三、for循环四、break语句五、continue语句六、break和continue对比七、循环结构总结一、内容简介 使用while循环编写重复...
    99+
    2022-11-12
  • Python 循环结构详解
    目录一、While循环二、While…else…循环三、for循环四、for…else…循环五、循环体结束语句六、嵌套循环前言...
    99+
    2022-11-13
  • 数据结构TypeScript之链表实现详解
    目录链表结构特点面向对象方法封装链表构造函数基本单元:链表节点主体:链表查找节点增加节点删除节点链表结构特点 链表是线性表的其中一种,用于存储有固定顺序的元素。而元素之间会通过&r...
    99+
    2023-01-30
    TypeScript数据结构链表 TypeScript数据结构
  • python数据结构之链表(linked
    目录 基础 知识 1.1 链表的基本结构 1.2 节点类和链表节点的定义 1.3 顺序打印和逆序打印 链表的基本操作 2.1 计算链表长度 2.2 从前,后插入数据 2.3 查找与删除 参考 1.基础 知识 1....
    99+
    2023-01-31
    数据结构 链表 python
  • java数据结构基础:循环链表和栈
    目录循环链表:实现思路:代码实现:栈:实现思路:代码实现:总结循环链表: 与单链表的最后一个节点的指针域为null不同,循环链表的最后一个节点的指针指向头结点 实现思路: 初始化时将...
    99+
    2022-11-12
  • JavaScript数据结构之双向链表和双向循环链表的示例分析
    这篇文章主要为大家展示了“JavaScript数据结构之双向链表和双向循环链表的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JavaScript数据结...
    99+
    2022-10-19
  • Python数据结构之翻转链表
    翻转一个链表 样例:给出一个链表1->2->3->null,这个翻转后的链表为3->2->1->null 一种比较简单的方法是用“摘除法”。就是先新建一个空节点,然后...
    99+
    2022-06-04
    数据结构 链表 Python
  • Python 数据结构之旋转链表
    题目描述:给定一个链表,旋转链表,使得每个节点向右移动k个位置,其中k是一个非负数 样例:给出链表1->2->3->4->5->null和k=2;返回4->5->...
    99+
    2022-06-04
    数据结构 链表 Python
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作