如何解决删除链表元素 - 递归问题
我正在尝试使用辅助递归函数解决“从具有值 val 的整数链表中删除所有元素”问题,但它不起作用。
Example:
Input: 1->2->6->3->4->5->6,val = 6
Output: 1->2->3->4->5
我的解决方案:
# DeFinition for singly-linked list.
# class ListNode:
# def __init__(self,val=0,next=None):
# self.val = val
# self.next = next
class Solution:
def checkHead(self,head,val):
if head.val == val and head.next:
head = head.next
self.checkhead(head,val)
elif head.val and not head.next:
head = None
return head
def removeElements(self,head: ListNode,val: int) -> ListNode:
if not head:
return head
head = self.checkHead(head,val)
if not head:
return head
curr = head
while curr and curr.next:
if curr.next.val == val:
curr.next = curr.next.next
curr = curr.next
return None
失败的测试用例:
Input: 1->1,val = 1
Output: []
当我将递归 checkHead
函数的值返回为 head = self.checkHead(head,val)
时,它指出 head
等于“1”,但是当我调试它时,我可以看到程序从 { 返回{1}} 为 checkHead
。我想知道问题出在哪里。
解决方法
根本缺陷
试图通过迭代和变异来解决这个问题存在一个根本性的缺陷。尽你所能,remove_elements
可以调整head
和next
属性,但它不能改变{{1 }} 到 ListNode
-
None
我们希望看到 w = node(1)
print(w) # ListNode { head = 1,next = None }
remove(w,1)
print(w) # ???
作为结果,但如果没有以下至少一项,这是不可能的 -
- 函数调用必须改变
-
None
的数据结构必须更改
假设要保持 ListNode
的形状,我们必须将函数调用调整为 -
ListNode
现在使用 w = node(1)
print(w)
w = remove_elements(w,1) # <- reassign with result of call
print(w)
的返回值,我们可以捕捉到 remove_elements
被降级为普通 ListNode
的场景。使用您的第一个示例 -
None
p = node(6,node(1,node(2,node(6,node(3,node(4,node(5,node(6))))))))
print(to_str(p))
p = remove_elements(p,6) # <- reassign p
print(to_str(p))
你的第二个例子 -
6->1->2->6->3->4->5->6->∅
1->2->3->4->5->∅
q = node(1,node(1))
print(to_str(q))
q = remove_elements(q,1) # <- reassign q
print(to_str(q))
这是一个可变单向链表的实现 -
1->1->∅
∅
了解你的出身
这个问题被标记为 class node:
def __init__(self,head,next = None):
self.head = head
self.next = next
def remove_elements(t,val):
# load stack
s = []
while t:
if t.head != val:
s.append(t)
t = t.next
# unload stack
r = None
while s:
q = s.pop()
q.next = r
r = q
# return
return r
def to_str(t):
r = ""
while t:
r = r + str(t.head) + "->"
t = t.next
return r + "∅"
,这是理所当然的。链表是一种递归数据结构,选择一个递归程序来处理它可以使我们的程序结构与我们的数据结构协调一致。然而,递归是一种函数式遗产,因此将它与函数式风格结合使用会产生最好的结果。这意味着避免诸如突变、变量重新分配和其他副作用之类的事情。不是破坏现有值,而是创建一个新值 -
- 如果输入列表
recursion
为空,则已达到基本情况。返回空列表 - (归纳)输入非为空;它至少有一个元素。如果第一个元素
t
与要移除的值t.head
匹配,则返回子问题的结果val
- (归纳)输入不为空并且第一个元素不匹配要删除的值。构造一个新的列表节点
t.next
和子问题的结果t.head
-
t.next
对于你的第一个例子 -
def remove_elements(t,val):
if not t:
return None # 1
elif t.head == val:
return remove_elements(t.next,val) # 2
else:
return ListNode(t.head,remove_elements(t.next,val)) # 3
p = node(6,node(6))))))))
print(to_str(p))
print(to_str(remove_elements(p,6)))
使用您的其他示例 -
6->1->2->6->3->4->5->6->∅
1->2->3->4->5->∅
q = node(1,node(1))
print(to_str(q))
print(to_str(remove_elements(q,1)))
这是一个不可变(持久)单向链表的实现 -
1->1->∅
∅
注意
不可变实现不会改变现有值。这意味着创造了新的价值 -
class node:
def __init__(self,val):
if not t:
return None
elif t.head == val:
return remove_elements(t.next,val)
else:
return node(t.head,val))
def to_str(t):
if not t:
return "∅"
else:
return str(t.head) + "->" + to_str(t.next)
当我们编写 p = node(6,node(6))))))))
q = remove_elements(p,6)
时,会创建一个 new 列表并将其存储到 q = remove_elements(p,6)
并且 q
不变 -
p
print(to_str(q))
print(to_str(p))
解开
请注意 1->2->3->4->5->∅ # q is a new list
6->1->2->6->3->4->5->6->∅ # p is unchanged
的每个实现都是一个普通的函数,并且与类分离。如果您的程序需要基于类的解决方案,它就变成了我们普通函数的简单包装器 -
remove_elements
,
如果您打算使用递归,您可以使用它随时重新链接元素。这只需要 removeElements 函数本身来递归:
def removeElements(self,val):
if not head: return None # end of list
if head.val == val:
head = self.removeElements(head.next,val) # remove head
else:
head.next = self.removeElements(head.next,val) # keep head
return head
,
以下是您代码中的问题:
-
当您递归调用
checkHead
时,您不会捕获它返回的值,因此递归调用是无用的。它唯一的效用在于它返回的。因此,就像您在removeElements
中已经正确执行一样,您还应该在此处将返回值分配给head
:head = self.checkhead(head,val)
-
elif head.val
是错误的。它应该是elif head.val == val
。很遗憾您根本需要这个elif
。如果您让if
条件(在其上方)测试head
而不是head.next
,您根本不需要elif
案例。 -
while curr
循环不能很好地处理搜索值连续出现两次的情况。在这种情况下,您还将cur
移动到cur.next
,下一次迭代将不会查看cur.val
,而是查看cur.next.val
。因此永远不会检查cur
处的值。所以...你应该在cur = cur.next
块中执行else
。 -
最后的
return
是错误的。您不应返回None
,因为while cur
循环可能会在列表中保留一些节点。所以返回head
。
通过这些更改,实际上也不需要进行两次 if not head
检查:现在可以省略两者。
这是更正后的代码:
class Solution:
def checkHead(self,val):
# Check that head is not None,instead of checking head.next
if head and head.val == val:
# Should use the value returned by recursive call
head = self.checkHead(head.next,val)
return head
def removeElements(self,head: ListNode,val: int) -> ListNode:
head = self.checkHead(head,val)
curr = head
while curr and curr.next:
if curr.next.val == val:
curr.next = curr.next.next
else: # Only move to next when no match
curr = curr.next
return head # Should return head,not None
这会起作用。尽管如此,看到递归和迭代代码的混合还是有点奇怪,这也是为什么除了 removeElements
之外还需要一个单独的方法的原因。你实际上可以用递归来做所有的事情:
class Solution:
def removeElements(self,val: int) -> ListNode:
if head:
if head.val == val:
return self.removeElements(head.next,val)
else:
head.next = self.removeElements(head.next,val)
return head
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。