203. 移除链表元素
题意:删除链表中等于给定值 val 的所有节点。
示例 1: 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2: 输入:head = [], val = 1 输出:[]
示例 3: 输入:head = [7,7,7,7], val = 7 输出:[]
链表一直是处理的不太好的数据结构,太久没处理过,第一次做链表题容易直接定义一个临时的cur节点变量等于需要判断的节点,由于是在单向链表的场景下,如果判断出该节点需要被删除,那么我们无法对上一个节点进行操作(需要让【该删除的节点】的上一个节点指向【该删除的节点】的下一个节点),这样就无法继续了。
所以思路就是上述括号这样,但是代码的完成和完整性需要考虑的仔细一点,在代码实现的时候,应该让临时节点cur的next指向被判断的节点,也就是cur应该作为【被判断节点】的前一个节点,判断删除的时候,直接让cur节点next绕过被删除节点即可。(python自动释放,C++手动释放被删除节点)
以下为代码的实现:
# 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: Optional[ListNode], val: int) -> Optional[ListNode]:
while head and head.val == val:
head = head.next #当头部节点为被删除节点,头部改为下一个节点,直到不是被删除节点(可能全是,最后剩下空链表)
cur = ListNode(next = head)
while cur.next:
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return head
代码随想录提供【虚拟头结点】的方法:
那么可不可以 以一种统一的逻辑来移除 链表的节点呢。
其实可以设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。
理解下来发现和我自己的实现也是大同小异的思路,但是更加规范化了,也更公式化,有利于后续类似题目提高做题速度,记录一下:
虚拟头节点法
# 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: Optional[ListNode], val: int) -> Optional[ListNode]:
# 创建虚拟头部节点以简化删除过程
dummy_head = ListNode(next = head)
# 遍历列表并删除值为val的节点
current = dummy_head
while current.next:
if current.next.val == val:
current.next = current.next.next
else:
current = current.next
return dummy_head.next
707.设计链表
题意:
在链表类中实现这些功能:
- get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
- addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
- addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
- addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
- deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
思路还是使用推荐的虚头部节点,在ListNode结构中设置val和next,在List中设置虚头部dummyhead和size(为了判断索引是否正确)。
Debug问题主要出在:
- 声明cur节点的方式不太好,使用了构造函数,反而导致出现很多问题,还是掌握的不太好,直接令其等于dummyhead其实就可以了。至于事等于dummyhead还是dummyhead.next,根据函数的作用和目标来理解即可。
- 对数据结构和对象的构造函数、属性、self的使用等不熟练,这一题强化了自己的掌握度。
- 虽然最后debug出来了,但还是有很多习惯不太好,应借鉴规范代码改进。
- Debug中的一个问题补充一段AI的说明改正:ListNode=None 出现在 MyLinkedList 类的构造函数中,这是一个错误的用法。ListNode 应该是一个类,而不是一个值。您可能想要将 None 赋给 dummyhead.next,表示链表开始时为空。
在 MyLinkedList 类中,dummyhead 是一个哑节点(也称为哨兵节点),它不是链表的一部分,但是作为链表的头部,使得我们可以统一处理头部和中间节点的添加和删除操作。哑节点不存储任何实际的数据,它的 next 指针指向链表的第一个实际节点。
以下为代码实现:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class MyLinkedList:
def __init__(self):
#def __init__(self, ListNode=None) # 自己瞎补充的None导致debug了很久。。也没想起来为啥自己这么写
# self.head = ListNode # 根据代码随想录的指引,还是使用虚头部,使用了虚头部后head多余了
self.dummyhead = ListNode() # 初始化0
self.size = 0
def get(self, index: int) -> int:
if index<0 or index>=self.size:
return -1
# cur = ListNode(next=self.dummyhead.next) # 直接使用 self.dummyhead 而不是创建一个新的 ListNode 实例。
cur = self.dummyhead.next
for i in range(index):
cur = cur.next
#return cur.next.val
return cur.val
def addAtHead(self, val: int) -> None:
newHead = ListNode(val=val)
newHead.next = self.dummyhead.next
self.dummyhead.next = newHead
self.size += 1
def addAtTail(self, val: int) -> None:
#cur = ListNode(next=self.dummyhead.next) #直接使用 self.dummyhead 而不是创建一个新的 ListNode 实例。
cur = self.dummyhead.next
#for i in range(self.size): # 找尾部直接用while不用for
# cur = cur.next
while cur.next:
cur = cur.next
newTail = ListNode(val=val)
#cur.next.next = newTail
cur.next = newTail
self.size += 1
def addAtIndex(self, index: int, val: int) -> None:
if index > self.size:
return
#cur = ListNode(next=self.dummyhead.next) #直接使用 self.dummyhead 而不是创建一个新的 ListNode 实例。
cur = self.dummyhead
for i in range(index):
cur = cur.next
newNode = ListNode(val, cur.next)
cur.next = newNode
self.size += 1
def deleteAtIndex(self, index: int) -> None:
if index<self.size:
#cur = ListNode(next=self.dummyhead.next) #直接使用 self.dummyhead 而不是创建一个新的 ListNode 实例。
cur = self.dummyhead
for i in range(index):
cur = cur.next
cur.next = cur.next.next
self.size -= 1
# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)
规范代码:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class MyLinkedList:
def __init__(self):
self.dummy_head = ListNode()
self.size = 0
def get(self, index: int) -> int:
if index < 0 or index >= self.size:
return -1
current = self.dummy_head.next
for i in range(index):
current = current.next
return current.val
def addAtHead(self, val: int) -> None:
self.dummy_head.next = ListNode(val, self.dummy_head.next)
self.size += 1
def addAtTail(self, val: int) -> None:
current = self.dummy_head
while current.next:
current = current.next
current.next = ListNode(val)
self.size += 1
def addAtIndex(self, index: int, val: int) -> None:
if index < 0 or index > self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next = ListNode(val, current.next)
self.size += 1
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index >= self.size:
return
current = self.dummy_head
for i in range(index):
current = current.next
current.next = current.next.next
self.size -= 1
# Your MyLinkedList object will be instantiated and called as such:# obj = MyLinkedList()# param_1 = obj.get(index)# obj.addAtHead(val)# obj.addAtTail(val)# obj.addAtIndex(index,val)# obj.deleteAtIndex(index)
基本没有大问题,和规范代码思路基本一致。
这次没时间写多一个双向链表的实现了,先将规范代码记录,理解之后后面抽一天回来闭卷补了。
(版本二)双链表法
class ListNode:
def __init__(self, val=0, prev=None, next=None):
self.val = val
self.prev = prev
self.next = next
class MyLinkedList:
def __init__(self):
self.head = None
self.tail = None
self.size = 0
def get(self, index: int) -> int:
if index < 0 or index >= self.size:
return -1
if index < self.size // 2: # 整数除法,确保得出的数是整数
current = self.head
for i in range(index):
current = current.next
else:
current = self.tail
for i in range(self.size - index - 1):
current = current.prev
return current.val
def addAtHead(self, val: int) -> None:
new_node = ListNode(val, None, self.head)
if self.head:
self.head.prev = new_node
else:
self.tail = new_node
self.head = new_node
self.size += 1
def addAtTail(self, val: int) -> None:
new_node = ListNode(val, self.tail, None)
if self.tail:
self.tail.next = new_node
else:
self.head = new_node
self.tail = new_node
self.size += 1
def addAtIndex(self, index: int, val: int) -> None:
if index < 0 or index > self.size:
return
if index == 0:
self.addAtHead(val)
elif index == self.size:
self.addAtTail(val)
else:
if index < self.size // 2:
current = self.head
for i in range(index - 1):
current = current.next
else:
current = self.tail
for i in range(self.size - index):
current = current.prev
new_node = ListNode(val, current, current.next)
current.next.prev = new_node
current.next = new_node
self.size += 1
def deleteAtIndex(self, index: int) -> None:
if index < 0 or index >= self.size:
return
if index == 0:
self.head = self.head.next
if self.head:
self.head.prev = None
else:
self.tail = None
elif index == self.size - 1:
self.tail = self.tail.prev
if self.tail:
self.tail.next = None
else:
self.head = None
else:
if index < self.size // 2:
current = self.head
for i in range(index):
current = current.next
else:
current = self.tail
for i in range(self.size - index - 1):
current = current.prev
current.prev.next = current.next
current.next.prev = current.prev
self.size -= 1
# Your MyLinkedList object will be instantiated and called as such:# obj = MyLinkedList()# param_1 = obj.get(index)# obj.addAtHead(val)# obj.addAtTail(val)# obj.addAtIndex(index,val)# obj.deleteAtIndex(index)
206.反转链表
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
由于正常的思路过于好想,新建一个链表的思路肯定不是所考察的内容,所以第一时间也懒得去实现了。直接看到了今天的提示:双指针和递归。
试着自己实现双指针的思路:
一个prev指针,一个cur指针,思路肯定是让cur指向prev,然后再往后平移,依次让节点指向往前指,但是由于只看了双指针法的字面意思,一直认定只需要两个指针就能实现,所以虽然自己想到了用tem存储【原】cur.next的位置,但还是限制自己去用这个方法存放,后面发现好像无论如何会使得链表中断,忍不住看了解析,发现确实还是用了temp,看来这个用来存放的节点变量不能当作指针,有点苦笑不得。那么实现就简单了,明白思路后两分钟就速通了代码:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
prev = None
cur = head
while cur:
tem = cur.next
cur.next = prev
prev = cur
cur = tem
return prev
看了视频,卡哥讲的太清晰易懂了,把递归也实现一下。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverse(self, prev:ListNode, cur:ListNode) -> ListNode: #在类中新定义一个def,定义这行括号里需要self,用于构造函数,由于返回的是一个ListNode,括号后需要->ListNode
if not cur:
return prev # 递归返回条件是cur为空
tem = cur.next
cur.next = prev
return self.reverse(cur, tem) # 调用递归函数需要self.reverse() 括号里不需要带self
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
prev = None
cur = head
return self. reverse(prev, cur) # 调用类中其他def需要用self
标签:index,cur,val,self,current,随想录,next,链表,移除 From: https://blog.csdn.net/weixin_47681529/article/details/142220247