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步