链表基础知识与题目整理(Python)

1.链表简介

1.1 链表定义

链表(Linked List): 一种线性表数据结构。它使用一组任意的存储单元(可以是连续的,也可以是不连续的),来存储一组具有相同类型的数据。
比如单链表
在这里插入述
如上图所示,链表通过将一组任意的存储单元串联在一起。

链表节点: 每个数据元素占用若干存储单元的组合称为一个链接点。

链表节点的存储内容: 数据元素的值+后继指针(后继指针是指出这个数据元素在逻辑关系上的直接后继元素所在链节点的地址)。

数据之间的逻辑关系:逻辑关系是通过后继指针来简介反映的。逻辑上相邻的数据数素在物理地址上可能相邻,也可能不相邻。其在物理地址上的表现是随机的。

链表的优缺点
优点:存储空间不必事先分配,在需要存储空间的时候可以临时申请,不会造成空间的浪费;一些操作的时间效率远比数组高(插入、移动、删除元素等)。
缺点:不仅数据元素本身的数据信息需要占用存储空间,后继指针也需要占用存储空间,链表结构比数组结构的空间开销大。

1.2 双向链表

双线链表(Doubly Linked List:链表的一种,也叫做双链表。它的每个链节点中有两个指针,分别指向后继前驱
在这里插入图片描述

1.3 循环链表

循环链表(Circular Linked List):链表的一种。它的最后一个链节点指向头节点,形成一个环。
在这里插入图片描述

2.链表的基本操作

以单链表为例介绍数据结构的增、删、改、查4种情况。

2.1 链表的结构定义

链表是由链节点通过next链接而构成的,所以需要先定义一个简单的链节点类,即ListNode类:
ListNode类使用成员:val表示数据元素的值,使用指针标量next表示后继指针。
在创建空链表时,只需要把相应的链表头节点变量设置成空链接即可。在Python里可以将其设置为None,其它语言也有类似值。

# 链节点类
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val       # 数据域
        self.next = next     # 指针域

# 链表类
class LinkedList:
    def __init__(self):
        self.head = None  # 初始化头节点

2.2 建立一个线性链表

建立一个线性链表的过程是:根据线性表的数据元素动态生成链节点,并依次将其连接到链表种。具体做法如下:

  • 从第1个数据元素开始依次获取表中的数据元素。
  • 每取一个数据元素,就为该数据生成一个新节点,将新节点插入到链表的尾部。
  • 插入完毕之后返回第1个链节点的地址

建立一个线性链表的时间复杂度为 O(n),n 为线性表长度。

建立一个线性链表的代码如下

# 根据 data 初始化一个新链表
def create(self, data):
    self.head = ListNode(0)
    cur = self.head        
    for i in range(len(data)):
        node = ListNode(data[i])
        cur.next = node
        cur = cur.next

2.3 求线性链表的长度

线性链表的长度被定义为链表中包含的链节点的个数。求线性链表的长度操作只需要使用一个可以顺着链表指针移动的指针变量 cur 和一个计数器 count。具体做法如下:

  • 让指针变量 cur 指向链表的第 1 个链节点。
  • 然后顺着链节点的 next 指针遍历链表,指针变量 cur 每指向一个链节点,计数器就做一次计数。
  • 等 cur 指向为空时结束遍历,此时计数器的数值就是链表的长度,将其返回即可。

求线性链表长度的时间复杂度为 O(n),n 为线性表长度。

求线性链表长度

# 获取链表长度
def length(self):
    count = 0
    cur = self.head
    while cur:
        count += 1
        cur = cur.next 
    return count

2.4查找链表中的元素

在链表中查找值为 val 的位置:链表不能像数组那样进行随机访问,只能从头节点 head 开始,沿着链表一个一个节点逐一进行查找。如果查找成功,返回被查找节点的地址。否则返回 None。

查找链表中的元素的时间复杂度为 O(n),n 为线性表长度。

查找链表中元素的代码如下

# 查找元素
def find(self, val):
    cur = self.head
    while cur:
        if val == cur.val:
            return cur
        cur = cur.next
    return None

2.5插入元素

链表中插入元素操作分为三种:
第一种:链表头插入元素:在链表第1个链接点之前插入val的链接点。
第二种:链表尾部插入元素:在链表最后1个链节点之后插入值为val的链节点。
第三种:链表中间插入元素:在链表第i个链节点之间插入值为val的链接点。

2.5.1 链表头插入元素

算法实现的步骤为:
第一步:先创建一个值为val的链节点node。
第二步:将node的next指针指向链表的头节点head。
第三步:再将链表的头head指向node
如下图所示:
在这里插入图片描述
因为表头插入链节点与链表的长度无关系,所以该算法的时间复杂度为O(1)。
代码如下:

# 头部插入元素
def insertFront(self, val):
    node = ListNode(val)   # 创建node
    node.next = self.head  # 将原先头地址赋给新node的next
    self.head = node       # 将node的地址作为整个链表头

2.5.2 尾部插入元素

实现步骤:
第一步:创建一个值为val的链节点node
第二步:使用指针cur指向链表的头节点head
第三步:通过链节点的next指针移动cur指针,从而遍历链表,直到cur.next == None。
第四步:令cur.next指向将新的链节点node
在这里插入图片描述
因为将 cur 从链表头部移动到尾部的操作次数是 n 次,所以该算法的时间复杂度是 O(n)。

# 尾部插入元素
def insertRear(self, val):
    node = ListNode(val)
    cur = self.head
    while cur.next:    # 将cur移到最后值的位置
        cur = cur.next
    cur.next = node    # 将新的位置地址赋给旧链表最后一个数的next

2.5.3 中间插入元素

实现步骤如下:
第一步:使用指针变量cur和一个计数器count。令cur指向链表的头节点,count初始值赋值为0。
第二步:沿着链节点的next指针遍历链表,指针变量cur每指向一个链节点,计数器就做一次计数。
第三步:当count==index-1时,说明遍历道了第index-1个链节点,此时停止遍历。
第四步:创建一个为val的链节点node。
第五步:将node.next指向cur.next。
第六步:令cur.next指向node。
详细内容如下图所示:
在这里插入图片描述
在这里插入图片描述
代码如下:

# 中间插入元素
def insertInside(self, index, val):
    count = 0
    cur = self.head
    # 先找到要插入的位置
    while cur and count < index - 1: 
        count += 1
        cur = cur.next
    # 如果不存在,报错  
    if not cur:
        return 'Error'
    # 创建node
    node = ListNode(val)
    # node.next 指向cur.next
    node.next = cur.next   # cur.next原先存储的是下一个值的地址
    
    cur.next = node       
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值