python双向循环链表实例详解

Python双向循环链表实例详解

本文介绍如何通过Python实现双向循环链表,让读者更好地理解链表的概念和应用。全文包含以下内容:

  1. 什么是双向循环链表?
  2. 如何实现双向循环链表?
  3. 双向循环链表的应用场景
  4. Python双向循环链表的示例

什么是双向循环链表?

双向循环链表是一种常见的数据结构,它由一系列节点组成,每个节点包含两个指针,分别指向前驱节点和后继节点。头节点的前驱节点指向尾节点,尾节点的后继节点指向头节点,形成了一个循环结构。

相比于单向链表,双向循环链表可以快速地访问前驱节点,因此更加方便实现双向遍历和双向查找等操作。同时,在删除节点时也更加方便。

如何实现双向循环链表?

实现双向循环链表,需要定义节点类和链表类,具体代码如下:

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

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

    def add_first(self, value):
        node = Node(value)

        if self.size == 0:
            self.head = self.tail = node
        else:
            node.next = self.head
            self.head.prev = node
            self.head = node

        self.size += 1

    def add_last(self, value):
        node = Node(value)

        if self.size == 0:
            self.head = self.tail = node
        else:
            node.prev = self.tail
            self.tail.next = node
            self.tail = node

        self.size += 1

    def remove_first(self):
        if self.size == 0:
            raise Exception("Linked list is empty")
        elif self.size == 1:
            node = self.head
            self.head = self.tail = None
        else:
            node = self.head
            self.head = node.next
            self.head.prev = None

        self.size -= 1
        return node.data

    def remove_last(self):
        if self.size == 0:
            raise Exception("Linked list is empty")
        elif self.size == 1:
            node = self.head
            self.head = self.tail = None
        else:
            node = self.tail
            self.tail = node.prev
            self.tail.next = None

        self.size -= 1
        return node.data

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

上面代码中,我们定义了一个Node类来表示链表的节点。每个节点包含三个属性:data表示存储的数据,prev表示前驱节点,next表示后继节点。

我们还定义了一个DoublyLinkedList类来管理整个链表。其中,headtail分别表示头结点和尾节点,size表示链表长度。

add_first方法和add_last方法分别在链表的头部和尾部添加节点。在添加节点时,需要判断链表是否为空,如果为空,则头结点和尾节点都指向新加入的节点;如果不为空,则需要更改头结点或者尾节点的前驱和后继指针。

remove_first方法和remove_last方法分别在链表的头部和尾部删除节点。在删除节点时,同样需要判断链表是否为空,如果为空则抛出异常;如果链表只有一个节点,则需要将头结点和尾节点都置为None;如果链表有多个节点,则需要找到要删除的节点,修改前驱和后继指针。

最后,我们实现了__iter__方法,以便支持对链表的迭代。

双向循环链表的应用场景

双向循环链表在实际应用中非常广泛,例如浏览器的“前进”和“后退”功能、游戏中的物品管理等等。

下面我们以实例的形式来说明如何使用双向循环链表,以此加深对该数据结构和算法的理解。

Python双向循环链表的示例

下面是一个简单的示例,我们使用双向循环链表来实现一个数码游戏。

在数码游戏中,玩家需要将九个数字拼成一个3x3的矩阵。玩家可以通过交换相邻的数字来完成拼图。这个过程中,我们需要实时记录拼图的状态,这时候就可以用到双向循环链表了。

class Puzzle:
    def __init__(self, numbers):
        self.board = DoublyLinkedList()
        self.moves = []
        for number in numbers:
            self.board.add_last(number)

    def move_left(self):
        index = 1
        current = self.board.head.next
        while current:
            if current.data == 0:
                break
            index += 1
            current = current.next

        if index % 3 == 1:
            raise Exception("Cannot move left")

        current.data, current.prev.data = current.prev.data, current.data
        self.moves.append("L")

    def move_right(self):
        index = 1
        current = self.board.head.next
        while current:
            if current.data == 0:
                break
            index += 1
            current = current.next

        if index % 3 == 0:
            raise Exception("Cannot move right")

        current.data, current.next.data = current.next.data, current.data
        self.moves.append("R")

    def move_up(self):
        index = 0
        current = self.board.head
        while current:
            if current.data == 0:
                break
            index += 1
            current = current.next

        if index < 3:
            raise Exception("Cannot move up")

        i = 0
        current = self.board.head
        while i < index - 3:
            current = current.next
            i += 1

        current.data, current.prev.data = current.prev.data, current.data
        self.moves.append("U")

    def move_down(self):
        index = 0
        current = self.board.head
        while current:
            if current.data == 0:
                break
            index += 1
            current = current.next

        if index > 5:
            raise Exception("Cannot move down")

        i = 0
        current = self.board.head
        while i < index + 3:
            current = current.next
            i += 1

        current.data, current.prev.data = current.prev.data, current.data
        self.moves.append("D")

    def print_board(self):
        for i, number in enumerate(self.board):
            print(number, end=" ")
            if i % 3 == 2:
                print()
        print()

上面这个代码中,我们首先定义了一个Puzzle类,这个类包含一个双向循环链表board来存储拼图状态。对于拼图中的每一块,我们都使用一个数字来表示,其中数字0表示空白块。

我们可以通过move_leftmove_rightmove_upmove_down方法来移动拼图的空白块,这里涉及到了判断拼图中每个块的位置关系,如果空白块可以移动,则交换空白块和它所在的块。我们用moves数组记录下每一步的移动方向。最后,我们还提供了print_board方法,用于打印当前拼图的状态。

现在我们可以通过以下代码来测试这个类的功能:

puzzle = Puzzle([2, 8, 3, 1, 6, 4, 7, 0, 5])

puzzle.move_right()
puzzle.print_board()
puzzle.move_right()
puzzle.print_board()
puzzle.move_down()
puzzle.print_board()
puzzle.move_down()
puzzle.print_board()
puzzle.move_left()
puzzle.print_board()
puzzle.move_up()
puzzle.print_board()
puzzle.move_left()
puzzle.print_board()
puzzle.move_up()
puzzle.print_board()

print(puzzle.moves)

以上就是Python实现双向循环链表的示例。本文中,我们首先介绍了什么是双向循环链表,然后讲解了如何使用Python来实现双向循环链表,最后,我们通过一个实际的数码游戏来展示双向循环链表的应用。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:python双向循环链表实例详解 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • 你真的懂C++中的namespace用法

    下面是我对于C++中namespace的详细讲解以及使用攻略。 C++中namespace的作用 在C++中,namespace(命名空间)的作用是解决命名冲突的问题。在大型程序中,由于文件或者库之间可能会存在相同的变量名或函数名,如果没有命名空间,容易导致程序出现错误。而使用命名空间,可以将同一组有关联的变量、类、函数等集合到一个namespace中,从而…

    other 2023年6月26日
    00
  • java控制台输入

    java控制台输入 在Java中,通常会需要从控制台输入数据。本文将详细介绍如何在Java中使用控制台输入。 使用Scanner类进行控制台输入 我们可以使用Java自带的Scanner类来从控制台获取输入。以下是一个基本的示例: import java.util.Scanner; public class ConsoleInputExample { pub…

    其他 2023年3月29日
    00
  • Performance 内存监控使用技巧详解

    Performance 内存监控使用技巧详解 在软件开发和系统管理中,监控内存的使用情况对于性能优化和故障排查非常重要。本攻略将详细介绍一些内存监控的使用技巧,帮助您更好地理解和优化系统的内存使用。 1. 监控内存使用的工具 1.1 top 命令 top 命令是一个常用的命令行工具,用于实时监控系统的各项指标,包括内存使用情况。以下是使用 top 命令监控内…

    other 2023年8月2日
    00
  • JDK9为何要将String的底层实现由char[]改成了byte[]

    JDK 9将String的底层实现由char[]改成了byte[]的原因 在JDK 9中,Java的String类的底层实现从使用char[]数组改为了使用byte[]数组。这个改变是为了提高内存使用效率和性能,并且在处理非拉丁字符时能够更好地支持Unicode编码。 1. 内存使用效率 使用byte[]数组作为String的底层实现可以减少内存使用量。在J…

    other 2023年8月2日
    00
  • 一键快速关机/重启和登出Win8的实用小技巧

    下面是关于“一键快速关机/重启和登出Win8的实用小技巧”的详细攻略。 一、快速关机和重启 方法一:使用快捷键 直接按下键盘上的「Win+I」快捷键,打开 Windows 8 的设置菜单; 点击「电源」选项,会出现「关机」和「重启」的选项,点击即可关机或重启。 方法二:使用命令行 打开命令提示符,可以通过 【Win + R】 键调出运行窗口,输入 cmd 后…

    other 2023年6月27日
    00
  • 红米手机开发者选项在哪?红米usb调试模式怎么打开?

    红米手机的开发者选项是一个隐藏的功能,需要进行特定的操作才能打开。在打开开发者选项后,用户可以进行诸如USB调试、在模拟器上运行应用程序等高级设置。 以下是详细讲解“红米手机开发者选项在哪?红米USB调试模式怎么打开?”的完整攻略: 步骤一:打开“关于手机”页面 首先打开你的红米手机的主屏幕,进入菜单。在菜单中找到“设置”选项,点击打开。然后在设置页面中,找…

    other 2023年6月26日
    00
  • easyui-textbox

    easyui-textbox的完整攻略 easyui-textbox是easyui框架中的一个文本框控件,它提供了丰富的功能和属性,可以满足各种文本输入需求。本文将介绍easyui-textbox的使用方法和常用属性,包括两个示例说明。 easyui-textbox的使用方法 在使用easyui-textbox时,我们需要引入easyui框架,并在HTML中…

    other 2023年5月9日
    00
  • Easyui在treegrid添加控件的实现方法

    下面是关于EasyUI在treegrid添加控件的实现方法的详细攻略: 1. 引入EasyUI相关文件 在网页中引入EasyUI相关文件,包括jQuery、EasyUI CSS和EasyUI JS。 <!–引入jQuery文件–> <script type="text/javascript" src="jq…

    other 2023年6月26日
    00
合作推广
合作推广
分享本页
返回顶部