【Python编程挑战】:单链表实现技巧与最佳实践

简介: 【Python编程挑战】:单链表实现技巧与最佳实践

🚀一、单链表的概念

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

链表是由一个个结点组成,每个结点之间通过链接关系串联起来,每个结点都有一个后继结点,最后一个结点的后继结点为空结点。

6e8c722fd495b5650b3a49da5aa43ad6_ac0116d28f8642768a708e6b00187a69.png


每个结点只设置一个指向后继结点的指针属性,这样的链表成为线性单项链接表,简称单链表;如果每个结点中设置两个指针属性,分别在于指向其前驱结点和后继结点,这样的链表称为线性双向链接表,简称双链表。无前驱结点或则后继结点的相应指针属性用常量None表示。

c21c5b75fa06f049b35c6b6a5e9de414_707c83f622584081b37d24f819df9e4b.png

注意:在Python中并不存在指针的概念,这里的指针属性实际上存放的是后继结点或者前驱结点的引用,但是为了表述方便仍然会采用 “指针” 一词。


🌈二、单链表的实现

⭐1. 单链表结点类

在单链表中,假定每个结点为LinkNode类对象,它包括存储元素的数据成员,这里用data表示,还包括存储后继结点的指针属性,这里用next表示。LinkNode的定义:

# 单链表结点类
class LinkNode:
    def __init__(self, data=None):
        self.data = data
        self.next = None

设计单链表类为LinkList,其中head属性为单链表的头结点,构造方法用于创建 这个头结点,并且设置head结点的next为空:

# 单链表类
class LinkList:
    def __init__(self):
        self.head = LinkNode()
        self.head.next = None


🎬2. 插入和删除操作

1、元素插入的概念

单向链表的元素插入,就是指给定一个索引 i 和一个元素 data,生成一个值为 data 的结点,并且插入到第 i 个位置上。


(1)、插入结点操作

p(即 pre)代表目前正在遍历的结点,当计数到 3 的时候,p 的后继结点 a (即 aft)也找到了,然后生成值为 5 的结点 vtx,将 p 的后继指向 vtx,将 vtx 的后继指向 a。

a85ee9fd4702920abdcc76205caea88c_fbce8de04672adad3655441896058ee5.gif

步骤:


  • 第1步:判断插入位置是否合法,如果不合法则抛出异常(比如:原本只有5个元素,给定的索引是100,那显然这个位置是不合法的)。
  • 第2步:对给定的元素,生成一个链表结点。
  • 第3步:如果插入位置是 0,则直接把生成的结点的后继结点,设置为当前的链表头结点,并且把生成的结点设置为新的链表头。
  • 第4步:如果插入位置不是 0,则遍历到插入位置的前一个位置,把生成的结点插入进来。
  • 第5步:更新链表的大小,即对链表的大小执行加一操作。

头插法:

# 头插法: 由数组a整体建立单链表
def CreateListF(self, a):
    for i in range(0, len(a)):
        s = LinkNode(a[i])
        s.next = self.head.next
        self.head.next = s

尾插法:

# 尾插法: 由数组a整体建立单链表
def CreateListR(self, a):
    t = self.head
    for i in range(0, len(a)):
        s = LinkNode(a[i])
        t.next = s
        t = s
    t.next = None

任意位置插入:

# 在线性表中序号为i的位置插入元素e
def Insert(self, i, e):
    assert i >= 0
    s = LinkNode(e)
    p = self.geti(i - 1)
    assert p is not None
    s.next = p.next
    p.next = s

上述指针修改用Python语句描述:

s.next = p.next

p.next = s

注意:这两个语句的顺序不能颠倒,如果先执行p.next = s语句,会找不到指向插入位置的下一个结点,再执行s.next = p.next语句,相当于执行s.next = s,导致插入操作错误。


2、元素删除的概念

单向链表的元素删除,就是指给定一个索引 i,将从链表头开始数到的第 i 个结点删除。


(1)、删除结点操作

删除运算是删除单链表中p结点的后继结点,就是将p结点的next修改为其后继结点的后继结点。


cba0f8a05dd2832889bbf5e607dbf0c8_7b814e2f3d10d7ea06f6b3af5ef21b9c.gif

步骤:


  • 第1步:判断删除位置是否合法,如果不合法则抛出异常。
  • 第2步:如果删除位置为首个结点,直接把链表头更新为它的后继结点。
  • 第3步:如果删除位置非首个结点,则遍历到要删除位置的前一个结点,并且把前一个结点的后继结点设置为它后继的后继。
  • 第4步:更新链表的大小,也就是将链表的大小执行减一操作。
# 在线性表中删除序号i的位置的元素
def Delete(self, i):
    assert i >= 0
    p = self.geti(i - 1)
    assert p.next is not None
    p.next = p.next.next

💥3. 元素查找

1、元素查找的概念

单向链表的元素查找,是指在链表中查找指定元素 x 是否存在,如果存在则返回该结点,否则返回 NULL。由于需要遍历整个链表进行元素对比,所以查找的时间复杂度为 O(n) 。

9665913ecaca2e1a7ea72a50186593da_c1f67574018d60262632d682d1731f9f.gif

2、元素查找的步骤


  • 第1步:遍历整个链表,对链表中的每个元素,和指定元素进行比较,如果相等则返回当前遍历到的结点;
  • 第2步:如果遍历完整个链表,都没有找到相等的元素,则返回 NULL。
# 求序号i的元素
def __getitem__(self, i):
    assert i >= 0
    p = self.geti(i)
    assert p is not None
    return p.data

❤️4. 单向链表的元素索引

1、元素索引的概念

单向链表的元素索引,是指给定一个索引值 i,从链表头结点开始数,数到第 i 个结点并且返回它,时间复杂度 O(n)。

2、元素索引的步骤


  • 第1步:首先判断给定的索引是否合法,不合法就抛出异常。
  • :直接通过索引访问即可获得对应的元素。

# 查找第一个为e的元素的序号
def GetNo(self, e):
    j = 0
    p = self.head.next
    while p is not None and p.data != e:
        j += 1
        p = p.next
    if p is None:
        return -1
    else:
        return j
☔5. 单向链表的元素修改

1、元素修改的概念

单向链表的元素修改是指将链表中指定索引的元素更新为新的值。

2、元素修改的步骤

  • 第1步:直接通过索引访问即可获得对应的结点,修改成指定的值。

19124f910ddaeec95072f24ed5894dd3_3cf15161fdc981623aa3a24f3fcb672f.gif

# 设置序号为i的元素
def __setitem__(self, i, x):
    assert i >= 0
    p = self.geti(i)
    assert p is not None
    p.data = x

返回序号为i的结点

返回序号为i的结点

# 返回序号为i的结点
👊6. 输出单链表

顾名思义就是一次便利单链表中各数据结点并输出结点值。

# 输出线性表
def display(self):
    p = self.head.next
    while p is not None:
        print(p.data, end=' ')
        p = p.next
    print()

时间复杂度为O(n),n表示顺序表中的元素个数。

🚀三、完整代码

# 单链表结点类
class LinkNode:
    def __init__(self, data=None):
        self.data = data
        self.next = None


# 单链表类
class LinkList:
    def __init__(self):
        self.head = LinkNode()
        self.head.next = None

    # 头插法: 由数组a整体建立单链表
    def CreateListF(self, a):
        for i in range(0, len(a)):
            s = LinkNode(a[i])
            s.next = self.head.next
            self.head.next = s

    # 尾插法: 由数组a整体建立单链表
    def CreateListR(self, a):
        t = self.head
        for i in range(0, len(a)):
            s = LinkNode(a[i])
            t.next = s
            t = s
        t.next = None

    # 返回序号为i的结点
    def geti(self, i):
        p = self.head
        j = -1
        while j < i and p is not None:
            j += 1
            p = p.next
        return p

    # 在线性表的末尾添加一个元素e
    def Add(self, e):
        s = LinkNode(e)
        p = self.head
        while p.next is not None:
            p = p.next
        p.next = s

    # 返回长度
    def getsize(self):
        p = self.head
        cnt = 0
        while p.next is not None:
            cnt += 1
            p = p.next
        return cnt

    # 求序号i的元素
    def __getitem__(self, i):
        assert i >= 0
        p = self.geti(i)
        assert p is not None
        return p.data

    # 设置序号为i的元素
    def __setitem__(self, i, x):
        assert i >= 0
        p = self.geti(i)
        assert p is not None
        p.data = x

    # 查找第一个为e的元素的序号
    def GetNo(self, e):
        j = 0
        p = self.head.next
        while p is not None and p.data != e:
            j += 1
            p = p.next
        if p is None:
            return -1
        else:
            return j

    # 在线性表中序号为i的位置插入元素e
    def Insert(self, i, e):
        assert i >= 0
        s = LinkNode(e)
        p = self.geti(i - 1)
        assert p is not None
        s.next = p.next
        p.next = s

    # 在线性表中删除序号i的位置的元素
    def Delete(self, i):
        assert i >= 0
        p = self.geti(i - 1)
        assert p.next is not None
        p.next = p.next.next

    # 输出线性表
    def display(self):
        p = self.head.next
        while p is not None:
            print(p.data, end=' ')
            p = p.next
        print()

🚲四、代码验证

if __name__ == '__main__':
    L = LinkList()
    print('建立空单链表L')
    a = [1, 2, 3, 4, 5, 6]
    print('1~6创建L')
    L.CreateListR(a)
    print('L[长度 = %d]: ' % (L.getsize()), end=' '), L.display()
    print('插入6~10')
    for i in range(6, 11):
        L.Add(i)
    print('L[长度 = %d]: ' % (L.getsize()), end=' '), L.display()
    print('序号为2的元素 = %d' % (L[2]))
    print('设置序号为2的元素为20')
    L[2] = 20
    print('L[长度 = %d]: ' % (L.getsize()), end=' '), L.display()
    x = 6
    print('第一个值为%d的元素序号 = %d' % (x, L.GetNo(x)))
    n = L.getsize()
    for i in range(n - 2):
        print('删除首元素')
        L.Delete(0)
        print('L[长度 = %d]: ' % (L.getsize()), end=' '), L.display()

594c022f4f777cefa02652f1c9e070e0_b5c4608c519f4f74abe2c77b13c037f3.png

相关文章
|
28天前
|
人工智能 数据可视化 数据挖掘
探索Python编程:从基础到高级
在这篇文章中,我们将一起深入探索Python编程的世界。无论你是初学者还是有经验的程序员,都可以从中获得新的知识和技能。我们将从Python的基础语法开始,然后逐步过渡到更复杂的主题,如面向对象编程、异常处理和模块使用。最后,我们将通过一些实际的代码示例,来展示如何应用这些知识解决实际问题。让我们一起开启Python编程的旅程吧!
|
27天前
|
存储 数据采集 人工智能
Python编程入门:从零基础到实战应用
本文是一篇面向初学者的Python编程教程,旨在帮助读者从零开始学习Python编程语言。文章首先介绍了Python的基本概念和特点,然后通过一个简单的例子展示了如何编写Python代码。接下来,文章详细介绍了Python的数据类型、变量、运算符、控制结构、函数等基本语法知识。最后,文章通过一个实战项目——制作一个简单的计算器程序,帮助读者巩固所学知识并提高编程技能。
|
15天前
|
Unix Linux 程序员
[oeasy]python053_学编程为什么从hello_world_开始
视频介绍了“Hello World”程序的由来及其在编程中的重要性。从贝尔实验室诞生的Unix系统和C语言说起,讲述了“Hello World”作为经典示例的起源和流传过程。文章还探讨了C语言对其他编程语言的影响,以及它在系统编程中的地位。最后总结了“Hello World”、print、小括号和双引号等编程概念的来源。
102 80
|
4天前
|
Python
[oeasy]python055_python编程_容易出现的问题_函数名的重新赋值_print_int
本文介绍了Python编程中容易出现的问题,特别是函数名、类名和模块名的重新赋值。通过具体示例展示了将内建函数(如`print`、`int`、`max`)或模块名(如`os`)重新赋值为其他类型后,会导致原有功能失效。例如,将`print`赋值为整数后,无法再用其输出内容;将`int`赋值为整数后,无法再进行类型转换。重新赋值后,这些名称失去了原有的功能,可能导致程序错误。总结指出,已有的函数名、类名和模块名不适合覆盖赋新值,否则会失去原有功能。如果需要使用类似的变量名,建议采用其他命名方式以避免冲突。
27 14
|
12天前
|
人工智能 分布式计算 数据处理
云产品评测:MaxFrame — 分布式Python计算服务的最佳实践与体验
阿里云推出的MaxFrame是一款高性能分布式计算平台,专为大规模数据处理和AI应用设计。它提供了强大的Python编程接口,支持分布式Pandas操作,显著提升数据处理速度(3-5倍)。MaxFrame在大语言模型数据处理中表现出色,具备高效内存管理和任务调度能力。然而,在开通流程、API文档及功能集成度方面仍有改进空间。总体而言,MaxFrame在易用性和计算效率上具有明显优势,但在开放性和社区支持方面有待加强。
42 9
|
14天前
|
分布式计算 大数据 数据处理
技术评测:MaxCompute MaxFrame——阿里云自研分布式计算框架的Python编程接口
随着大数据和人工智能技术的发展,数据处理的需求日益增长。阿里云推出的MaxCompute MaxFrame(简称“MaxFrame”)是一个专为Python开发者设计的分布式计算框架,它不仅支持Python编程接口,还能直接利用MaxCompute的云原生大数据计算资源和服务。本文将通过一系列最佳实践测评,探讨MaxFrame在分布式Pandas处理以及大语言模型数据处理场景中的表现,并分析其在实际工作中的应用潜力。
49 2
|
27天前
|
小程序 开发者 Python
探索Python编程:从基础到实战
本文将引导你走进Python编程的世界,从基础语法开始,逐步深入到实战项目。我们将一起探讨如何在编程中发挥创意,解决问题,并分享一些实用的技巧和心得。无论你是编程新手还是有一定经验的开发者,这篇文章都将为你提供有价值的参考。让我们一起开启Python编程的探索之旅吧!
46 10
|
29天前
|
机器学习/深度学习 人工智能 数据挖掘
探索Python编程的奥秘
在数字世界的海洋中,Python如同一艘灵活的帆船,引领着无数探险者穿梭于数据的波涛之中。本文将带你领略Python编程的魅力,从基础语法到实际应用,一步步揭开Python的神秘面纱。
44 12
|
28天前
|
IDE 程序员 开发工具
Python编程入门:打造你的第一个程序
迈出编程的第一步,就像在未知的海洋中航行。本文是你启航的指南针,带你了解Python这门语言的魅力所在,并手把手教你构建第一个属于自己的程序。从安装环境到编写代码,我们将一步步走过这段旅程。准备好了吗?让我们开始吧!
|
29天前
|
关系型数据库 开发者 Python
Python编程中的面向对象设计原则####
在本文中,我们将探讨Python编程中的面向对象设计原则。面向对象编程(OOP)是一种通过使用“对象”和“类”的概念来组织代码的方法。我们将介绍SOLID原则,包括单一职责原则、开放/封闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则有助于提高代码的可读性、可维护性和可扩展性。 ####