Python 链表笔记


一直以来写leetcode题目时候,都是直接使用已经定义好的链表数据结构,那么问题是利用python如何构建自己的链表呢?如何进行相关链表操作呢?问题提出来之后,就来本文寻找答案。

链表结构定义

class ListNode:
	def __init__(self, x):
		self.val = x
		self.next = None
# 实例化 构建一个长度为1的链表
head = ListNode(0)

定义完了之后,在leetcode 中就用 指针指向头节点,
但是,看到输出链表都是以list 形式,例如长度为3的链表,输出[3, 2, 1], 也没有在 程序里指向,感到奇怪嘛?

构建链表

707. 设计链表是一个不错的例子,在python中设定链表类,例如 是单链表,随后设定如下操作:

  • 获取 index 的值
  • 在头部添加节点
  • 在索引处添加节点
  • 在尾部添加节点
  • 删除索引处节点

定义

class MyLinkedList:
    def __init__(self):
        self.size = 0 # 记录链表节点数
        self.head = ListNode(0) # 定义一个 伪头节点

这里使用伪头部节点,来使得当链表为空和不为空操作一样。这个时候对于链表的索引与长度记录有了困惑。
问:对于一个有头部伪节点的链表,它的索引范围是多少?
原链表 3->2->1->Null
索引 [0, 1, 2] 长度 3
有伪头节点链表 0->3->2->1->Null
索引 [0, 1, 2, 3] 长度 4

在索引处添加节点

    def addAtIndex(self, index: int, val: int) -> None:   
        if index > self.size: return # 超过索引,不添加; 如果等于长度,则在尾部添加
        if index < 0: index = 0 # 要求小于0 就在头部添加节点

        to_add = ListNode(val)
        curr = self.head  # 伪头节点
        # 0,... ,index-1
        for _ in range(index):
            # 指向下一节点,等同于指向索引节点,最大的索引= 长度-1
            curr = curr.next
        # 添加操作
        to_add.next = curr.next
        curr.next = to_add
        # 记录链表长度的变化
        self.size += 1 

以上是十分关键的操作,有了这一步,在头部添加节点在尾部添加节点功能调用这个函数就可以了。
接下来是获取 index 的值和在尾部添加节点,这里也有一些困惑:
获取 index 的值的for循环需要index + 1,而 在尾部添加节点 不需要,直接index,看看有啥差别?

获取 index 的值

因为这里的索引依旧是原链表的索引,不需要考虑加了伪头节点的链表。

    def get(self, index: int) -> int:
        if index < 0 or index >= self.size:
            return -1
        curr = self.head
        for _ in range(index+1): # 例子:index=0
            curr = curr.next
        return curr.val

删除索引处节点

    def deleteAtIndex(self, index: int) -> None:
        if index < 0 or index >= self.size: return # 为啥是index >= self.size
        self.size -= 1
        pre = self.head
        for _ in range(index): # 指针指向它的前一个节点
            pre = pre.next
        curr = pre.next
        pre.next = curr.next

我们的目标是当前节点,拜拜了~
综上,问题解决。

报错

AttributeError: ‘NoneType’ object has no attribute ‘val’

通过检查,发现索引超过范围,但是还是调用了curr.next,细想一下,None怎么会有next 或者val的属性?
解决方法:检查索引范围设定。

链表相关题型

移除元素

在链表中删除节点,就要知道删除的节点的前一个节点。对于头节点来说,它没有前一个节点,为了方便编程,添加一个伪头节点,让头节点的操作与其他节点的操作一样,是一个不错的方法。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
    	# 伪头节点 
        dummy = ListNode(next=head)
        cur = dummy
        while (cur.next!=None):
            if cur.next.val==val:
                # 删除操作
                cur.next = cur.next.next
            else:
                cur = cur.next
        return dummy.next

反转链表

206.反转链表
(未完待续)
初始化

        pre = None
        cur = head

迭代过程,记录当前结点cur,前一结点pre,后一结点nextt

        while cur!=None:
            nextt = cur.next
            cur.next = pre
            # 更新
            pre = cur
            cur = nextt

链表相交

面试题 02.07. 链表相交
找到相交的点,代表指针相等。

环状链表

142. 环形链表 II 题解
双指针,fast每次走2步,slow每次走1步

参考

代码随想录
707.官方题解

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值