数据结构3线性表——单链表(Python)

本文章是通过Python实现链表的一系列操作,具体的关于链表的一些基础知识点

详见:数据结构3线性表------单链表(C)-CSDN博客

其他数据结构内容可见:数据结构_Gu_shiwww的博客-CSDN博客

1 链表前言

1.1 节点的构建

Python中链表以及链表节点的构建均是通过class类来进行构建:

python 复制代码
# 定义一个类
class Node:
    """创建单链表节点"""
    def __init__(self, data=None):

        self.data = data  # 数据域
        self.next = None  # 引用域 ,引用指向自身结构体的类型(存放的下一个节点的地址)

定义一个Node类,通过构造函数__init__()来初始化两个类中的变量,一个是数据域data,一个是指针域next

1.2 遍历单向链表

1.2.1 遍历无头的单向链表

无头单向链表,即链表内的每一个节点的数据域的引用都是有效的

python 复制代码
current = self.head
while current:
    print(current.data)
    current = current.next

定义current指向链表中的头节点,while循环的条件就是判断current是否为空,若current有指向节点,则进入while循环进行current的移动

1.2.2 遍历带头的单向链表

带头的单向链表,头节点的数据域是无效的,但是指针域有效

python 复制代码
current = self.head
while current.next:
    current = current.next
    print(current.data)

因为头指针下一定有一个头节点,但此节点数据无效,故while的循环执行条件是查看current的next域是否有内容。若while循环条件成立,需要先将current的指针进行后移,之后再打印内部的数据。

【练习】构建几个节点,之间用next指针连接在一起,按照next指针域进行逐个打印(用不带头节点的方式)

python 复制代码
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None


a, b, c, d = Node('A'), Node('B'), Node('C'), Node('D')
a.next = b
b.next = c
c.next = d
current = a
while current:
    print(current.data)
    current = current.next

2 有头单向链表的操作函数

P.1 给出链表操作的各个函数接口

python 复制代码
class Node:
    """创建单链表节点"""

    def __init__(self, data=None, next_node=None):
        self.data = data
        self.next = next_node


class LinkedList:
    def __init__(self):
        # 初始化一个头节点,它不包含实际数据,仅作为链表的起始标志
        self.head = Node()

    # 1.判断单向链表是否为空 1代表空 0代表非空
    def is_empty(self):
        pass

    # 2.求单向链表长度的函数
    def length(self):
        pass

    # 3.向单向链表的指定位置插入数据
    # post 插入的位置 data插入的数据
    def insert(self, position, data):
        pass

    # 4.遍历单向链表
    def show(self):
        pass
    
    # 5.删除单向链表中指定位置的数据 post 代表的是删除的位置
    def delete_position(self, position):
        pass

    # 6.删除单向链表中出现的指定数据,data代表将单向链表中出现的所有data数据删除
    def delete_data(self, data):
        pass

    # 7.查找指定数据出现的位置 data被查找的数据 //search 查找
    def search_data(self, data):
        pass

    # 8.转置链表
    def reverse(self):
        pass

    # 9.清空单向链表
    def clear(self):
        pass

    # 10.修改指定位置的数据 post 被修改的位置 data修改成的数据
    def change_data(self, position, data):
        pass

P.2 判断单向链表是否为空

python 复制代码
# 1.判断单向链表是否为空 1代表空 0代表非空
def is_empty(self):
    return self.head.next is None

带头节点的单链表,判断链表是否为空,可以查看头节点后面是否还连有节点。self.head指向链表的头节点,通过next域访问到头节点的下一个节点,若next为空则链表为空。函数可以返回self.head.next is None的判断结果。注意因为是链表,可以无限增加长度,不需要判断链表是否为满。

P.3 求单向链表的长度

python 复制代码
# 2.求单向链表长度的函数
def length(self):
    current = self.head
    length = 0
    while current.next:
        current = current.next
        length += 1
    return length

在求链表长度的函数中,不能直接对self.head,即头指针进行挪用,因为头节点被挪用之后,链表前面的元素不能再通过遍历head指针得到,只能再定义一个变量current,通过移动current来遍历整个链表,后续的函数中会经常出现定义一个current变量指向头指针,

P.4 向指定位置插入数据

python 复制代码
# 2.向单向链表的指定位置插入数据
# post 插入的位置 data插入的数据
def insert(self, position, data):
    # 容错判断
    if position < 0 or position > self.length():
        print("insert error")
        return
    # node新节点
    new_node = Node(data)
    # 移动伪指针到插入位置的前一个位置
    current = self.head
    for _ in range(position):
        current = current.next
    # 插入动作(先连后面,再连前面)
    new_node.next = current.next
    current.next = new_node

首先进行容错判断,插入的位置,即下标要大于0并且小于链表的长度,判断完之后创建一个新的节点new_node,定义current指向头指针,通过for循环遍历到要插入元素的前面一个位置,然后通过next指针将元素插入

P.5 单链表的遍历

python 复制代码
# 3.遍历单向链表
def show(self):
    current = self.head
    while current.next:
        current = current.next
        print(current.data, end=' ')
    print()

遍历比较简单,因为是带头结点的单链表,注意while的判断条件是current.next,然后通过遍历逐个打印节点的data域

P.6 删除指定位置的数据

python 复制代码
# 5.删除单向链表中指定位置的数据 post 代表的是删除的位置
def delete_position(self, position):
    # 容错判断
    if self.is_empty() or position < 0 or position >= self.length():
        print("delete_position error")
        return
    current = self.head
    # current走到删除位置的前一个位置
    for _ in range(position):
        current = current.next
    # 删除动作
    current.next = current.next.next

首先容错判断,判断position是否合法,并且判断链表是否为空,若为空则无节点可删,用current指向头元素,for循环遍历current到被删除节点的前一个位置,删除该数据(在Python中无需手动free被断开的节点,是因为Python中有垃圾回收机制,若该节点无任何引用指向它,解释器会自动回收该处空间)

P.7 删除所有出现的指定数据

python 复制代码
# 6.删除单向链表中出现的指定数据,data代表将单向链表中出现的所有data数据删除
def delete_data(self, data):
    if self.is_empty():
        print("delete_data error")
        return
    current = self.head
    while current.next:
        if current.next.data == data:
            current.next = current.next.next
        else:
            current = current.next

首先容错判断,只需要判断是否为空链表,其次遍历整个链表(因为要删除所有对应的元素)用一个分支语句去执行操作,如果节点的data域等于传递的参数data,则执行删除操作,否则链表自动继续遍历

P.8 查找指定元素的下标

python 复制代码
# 9.查找指定数据出现的位置 data被查找的数据 //search 查找
def search_data(self, data):
    if self.is_empty():
        print("search_data error")
        return
    current = self.head
    position = 0
    while current.next:
        current = current.next
        if current.data == data:
            return position
        position += 1
    return -1

首先容错判断,空链表不可能存在指定元素,其次遍历整个链表,用一个变量position来记录当前遍历到的节点的下标,若某个节点的data域与传递的参数data相同,则返回下标position,否则整个链表遍历结束,返回-1

P.9 链表的转置

python 复制代码
# 10.转置链表
def reverse(self):
    # 断开前,保存头节点的下一个节点的地址
    current = self.head.next
    # 断开链表
    self.head.next = None
    # 遍历无头单向链表,把无头单向链表的节点头插到有头空链表中
    while current:
        # 提前将current的下一个节点保存起来
        next_node = current.next
        # 先连后面,再连前面, 将无头表的节点插入头结点的下一个位置
        current.next = self.head.next
        self.head.next = current
        # 将current移动,指向下一个节点
        current = next_node

P.10 清空单向链表

python 复制代码
# 11.清空单向链表
def clear(self):
    self.head.next = None

只要断开头节点后面的节点,Python会自动执行垃圾回收机制,回收后面的节点

P.11 修改指定位置的数据

python 复制代码
# 8.修改指定位置的数据 post 被修改的位置 data修改成的数据
def change_data(self, position, data):
    # 容错判断
    if self.is_empty() or position < 0 or position >= self.length():
        print("change_data error")
        return
    current = self.head
    for _ in range(position + 1):
        current = current.next
    current.data = data

首先进行容错判断,判空以及position的合法性

之后通过for循环将current遍历到要修改节点的位置,直接通过current.data访问节点的data域进行数据的修改

P.12 完整代码(可执行)

python 复制代码
class Node:
    """创建单链表节点"""

    def __init__(self, data=None, next_node=None):
        self.data = data
        self.next = next_node


class LinkedList:
    def __init__(self):
        # 初始化一个头节点,它不包含实际数据,仅作为链表的起始标志
        self.head = Node()

    # 2.向单向链表的指定位置插入数据
    # post 插入的位置 data插入的数据
    def insert(self, position, data):
        # 容错判断
        if position < 0 or position > self.length():
            print("insert error")
            return
        # node新节点
        new_node = Node(data)
        # 移动伪指针到插入位置的前一个位置
        current = self.head
        for _ in range(position):
            current = current.next
        # 插入动作(先连后面,再连前面)
        new_node.next = current.next
        current.next = new_node

    # 3.遍历单向链表
    def show(self):
        current = self.head
        while current.next:
            current = current.next
            print(current.data, end=' ')
        print()

    # 4.求单向链表长度的函数
    def length(self):
        current = self.head
        length = 0
        while current.next:
            current = current.next
            length += 1
        return length

    # 5.删除单向链表中指定位置的数据 post 代表的是删除的位置
    def delete_position(self, position):
        # 容错判断
        if self.is_empty() or position < 0 or position >= self.length():
            print("delete_position error")
            return
        current = self.head
        # current走到删除位置的前一个位置
        for _ in range(position):
            current = current.next
        # 删除动作
        current.next = current.next.next

    # 6.删除单向链表中出现的指定数据,data代表将单向链表中出现的所有data数据删除
    def delete_data(self, data):
        if self.is_empty():
            print("delete_data error")
            return
        current = self.head
        while current.next:
            if current.next.data == data:
                current.next = current.next.next
            else:
                current = current.next

    # 7.判断单向链表是否为空 1代表空 0代表非空
    def is_empty(self):
        return self.head.next is None

    # 8.修改指定位置的数据 post 被修改的位置 data修改成的数据
    def change_data(self, position, data):
        # 容错判断
        if self.is_empty() or position < 0 or position >= self.length():
            print("change_data error")
            return
        current = self.head
        for _ in range(position + 1):
            current = current.next
        current.data = data

    # 9.查找指定数据出现的位置 data被查找的数据 //search 查找
    def search_data(self, data):
        if self.is_empty():
            print("search_data error")
            return
        current = self.head
        position = 0
        while current.next:
            current = current.next
            if current.data == data:
                return position
            position += 1
        return -1

    # 10.转置链表
    def reverse(self):
        # 断开前,保存头节点的下一个节点的地址
        current = self.head.next
        # 断开链表
        self.head.next = None
        # 遍历无头单向链表,把无头单向链表的节点头插到有头空链表中
        while current:
            # 提前将current的下一个节点保存起来
            next_node = current.next
            # 先连后面,再连前面, 将无头表的节点插入头结点的下一个位置
            current.next = self.head.next
            self.head.next = current
            # 将current移动,指向下一个节点
            current = next_node

    # 11.清空单向链表
    def clear(self):
        pass
    # endif


if __name__ == '__main__':
    link_list = LinkedList()
    link_list.insert(0, 999)
    link_list.insert(1, 888)
    link_list.insert(2, 888)
    link_list.show()
    link_list.insert(0, 666)
    link_list.show()
    link_list.insert(1, 777)
    link_list.show()
    link_list.reverse()
    link_list.show()
    link_list.delete_position(0)
    link_list.show()
    link_list.insert(0, 666)
    link_list.show()
    link_list.delete_data(666)
    link_list.show()
    link_list.change_data(1,666)
    link_list.show()
    print(link_list.search_data(777))