目录
203.移除链表元素
思路
有两种解题办法:
- 使用虚拟头结点删除元素,使用虚拟头结点的好处是,在删除元素时,无需考虑对头结点特殊操作
- 使用原头结点进行操作
方法一:使用虚拟头结点删除元素
# 使用虚拟头结点进行删除元素
class Solution:
def removeElement(self,head:ListNode,val:int) -> ListNode:
dummyhead = ListNode(next=head)
cur = dummyhead
# 思考一下while cur和while cur.next的区别
# 在这里我首先想到的是cur,老师给的解题是cur.next
while cur.next:
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return dummyhead.next
方法二:使用原头结点删除元素
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
# 我们要取头结点,所以头结点一定不能为空
while head and head.val == val:
head = head.next
cur = head
while cur and cur.next:
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return head
心得收获
在使用虚拟头结点时,有几个问题要注意:
- 使用虚拟头结点while循环的条件我设为cur,解题思路是用cur.next,使用cur的话,在cur走到链表末尾的时候会报错,因为链表末尾指向NULL,再用cur.next.val会提示错误,因为NULL没有val这个属性,详见下图:
- 为什么使用头结点和不适用头结点while的条件不一样呢?
在不使用头结点时,我们没法判断cur以及cur.next(要判断cur.next是因为我们要取下一个节点的值比较,如果下一个节点不存在,我们取值相当于操作空指针,就会报错。和我上面的报错一样)是不是存在 。
在使用虚拟头结点时,cur先指向我们的虚拟头结点,此时cur一定是存在的,所以只需要判断cur.next即可,下一次循环同理,此时cur.next 变为了cur,如果存在的话,只需要判断cur.next即可 - 使用虚拟头结点时,为什么cur不是指向dummyhead.next?
首先我们要明白我们删除节点时,一定要知道它的上一个节点信息(单链表只能拿到下一个节点信息,获取不到上一个节点是什么),如果我们的cur指向的是原头结点,那删除原头结点时,我们没有办法知道头结点的上一个节点是什么 - 在while循环中,为什么cur = cur.next要写在else里面?为什么不是if条件判断完之后,直接
cur = cur.next?
如果写成和if条件一个层级的话,那在删除元素之后,我们会跳过要删除元素后面的节点判断
707.设计链表
思路
我没有使用虚拟头结点设计的链表,想好思路
方法一:不设虚拟头节点
class ListNode:
def __init__(self,val,next=None) -> None:
self.val = val
self.next = next
class MyLinkedList:
def __init__(self) -> None:
self.head = None
self.size = 0
# 获取节点的值
def get(self,index:int) -> int:
# 因为index是从0开始的,所以此处判断要大于等于链表长度
# 不然会报错,因为超出链表长度,最后会操作空指针
if index < 0 or index >= self.size:
return -1
cur = self.head
while index:
cur = cur.next
index -= 1
return cur.val
# 添加链表头节点
def addAtHead(self,val:int) -> None:
if self.size == 0:
self.head = ListNode(val)
else:
new_node = ListNode(val,self.head)
# 此处忘记加上self,类中调用变量或函数,一定一定一定要加self!!!
self.head = new_node
self.size += 1
# 在链表尾部添加元素
def addAtTail(self,val:int) -> None:
if self.size == 0:
# 其实这里可以调用添加头节点函数
self.head = ListNode(val)
# 此处忘记加上else判断,导致添加头节点之后,后面的代码又执行了一遍
else:
cur = self.head
while cur.next:
cur = cur.next
cur.next = ListNode(val)
self.size += 1
# 添加链表元素
def addAtIndex(self,index:int,val:int) -> None:
if index > self.size:
return
elif index <= 0:
# 此处最坑,因为添加完头节点之后,self.size已经加1,然后又继续执行后面的代码
# 导致self.size又加1
# 所以加上return操作,在添加头节点之后直接结束,判断尾节点添加同理
self.addAtHead(val)
return
elif index == self.size:
self.addAtTail(val)
return
else:
cur = self.head
while index-1:
cur = cur.next
index -= 1
new_node = ListNode(val,cur.next)
cur.next = new_node
self.size += 1
# 删除链表元素
def deleteAtIndex(self,index:int) -> None:
if index == 0:
self.head = self.head.next
elif index >= self.size or index < 0:
return
else:
cur = self.head
# 此处要index-1,因为index是从0开始的
while index-1:
cur = cur.next
index -= 1
cur.next = cur.next.next
self.size -= 1
方法二:使用虚拟头节点
class ListNode:
def __init__(self,val=0,next=None) -> None:
self.val = val
self.next = next
class MyLinkedList:
def __init__(self) -> None:
self.dummyhead = ListNode()
self.size = 0
# 获取节点的值
def get(self,index:int) -> int:
if index < 0 or index >= self.size:
return -1
cur = self.dummyhead
while index:
cur = cur.next
index -= 1
return cur.next.val
# 添加链表头节点
def addAtHead(self,val:int) -> None:
new_node = ListNode(val)
if self.size == 0:
self.dummyhead.next = new_node
else:
new_node.next = self.dummyhead.next
self.dummyhead.next = new_node
self.size += 1
# 在链表尾部添加元素
def addAtTail(self,val:int) -> None:
new_node = ListNode(val)
if self.size == 0:
self.dummyhead.next = new_node
else:
cur = self.dummyhead
while cur.next:
cur = cur.next
cur.next = new_node
self.size += 1
# 添加链表元素
def addAtIndex(self,index:int,val:int) -> None:
if index > self.size:
return
elif index == self.size:
self.addAtTail(val)
return
else:
new_node = ListNode(val)
cur = self.dummyhead
while index:
cur = cur.next
index -= 1
new_node.next = cur.next
cur.next = new_node
self.size += 1
# 删除链表元素
def deleteAtIndex(self,index:int) -> None:
if index < 0 or index >= self.size:
return
else:
cur = self.dummyhead
while index:
cur = cur.next
index -= 1
cur.next = cur.next.next
self.size -= 1
方法三:使用双链表
class ListNode:
def __init__(self,val = 0,next = None,pre=None) -> None:
self.val = val
self.next = next
self.pre = pre
class MyLinkedList:
def __init__(self) -> None:
self.head = None
self.tail = None
self.size = 0
def get(self,index:int) -> None:
if index < 0 or index >= self.size:
return -1
else:
if index > self.size // 2:
cur = self.tail
for i in range(self.size-index-1):
cur = cur.pre
else:
cur = self.head
for i in range(index):
cur = cur.next
return cur.val
# 添加链表头节点
def addAtHead(self,val:int) -> None:
new_node = ListNode(val)
if self.size == 0:
self.head = new_node
self.tail = new_node
else:
new_node.next = self.head
# 这里忘记写把原头节点的向前的指针给更改
self.head.pre = new_node
self.head = new_node
self.size += 1
# 在链表尾部添加元素
def addAtTail(self,val:int) -> None:
new_node = ListNode(val)
if self.size == 0:
self.head = new_node
self.tail = new_node
else:
cur = self.tail
cur.next = new_node
# 这里忘记把新尾结点的向前指针值更改
new_node.pre = cur
self.tail = new_node
self.size += 1
# 添加链表中间元素
def addAtIndex(self,index:int,val:int) -> None:
if index > self.size:
return
elif index <= 0:
self.addAtHead(val)
return
elif index == self.size:
self.addAtTail(val)
return
else:
new_node = ListNode(val)
if index > self.size // 2:
cur = self.tail
for i in range(self.size-index-1):
cur = cur.pre
else:
cur = self.head
for i in range(index):
cur = cur.next
new_node.next = cur
new_node.pre = cur.pre
cur.pre.next = new_node
cur.pre = new_node
self.size += 1
# 删除链表元素
def deleteAtIndex(self,index:int) -> None:
if index < 0 or index >= self.size:
return
elif index == 0:
if self.size == 1:
self.head = None
self.tail = None
else:
self.head = self.head.next
self.head.pre = None
elif index == self.size-1:
self.tail = self.tail.pre
self.tail.next = None
else:
if index > self.size // 2:
cur = self.tail
for _ in range(self.size-index-1):
cur = cur.pre
else:
cur = self.head
for _ in range(index):
cur = cur.next
cur.pre.next = cur.next
cur.next.pre = cur.pre
self.size -= 1
心得收获
目前只写了,不设虚拟头节点的代码,遇到的问题也都注释在代码里,这里就不过多赘述,后面写虚拟头节点和双链表遇到问题,再更新
更新:使用虚拟头结点写下来基本很顺畅,但是这次总是忘记在while循环中对索引减小
更新:使用双指针写下来更顺畅一些,对链表的操作更熟悉了
加油吖!相信会越来越熟悉
206.反转链表
思路
- 使用双指针
- 使用递归,目前对递归没有很深入的理解,先不做叙述,以免带偏
方法一: 双指针法
# 双指针法
class Solution:
def reverseList(self,head:ListNode) -> ListNode:
pre = None
cur = head
while cur:
tmp = cur.next
cur.next = pre
pre = cur
cur = tmp
return pre
方法二:递归法
# 递归法
class Solution:
def reverseList(self,head:ListNode) -> ListNode:
self.digui(head,None)
def digui(self,cur:ListNode,pre:ListNode) -> ListNode:
if cur == None:
return pre
tmp = cur.next
cur.next = pre
return self.digui(tmp,pre)
心得收获
为什么要设双指针并初始化,首先要反转链表,当前节点cur的cur.next方向要指向前一个节点pre,那头节点的反转之后变成尾节点指向None,所以pre初始化为None。
另外要注意断开连接之后,要使用的节点要提前保存到临时变量,以免断开连接找不到该节点。
关于递归解法,可以看卡哥视频,完全能理解到位。仅限本题思路