微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

删除链表元素 - 递归问题

如何解决删除链表元素 - 递归问题

我正在尝试使用辅助递归函数解决“从具有值 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 可以调整headnext属性,但它不能改变{{1 }} 到 ListNode -

None

我们希望看到 w = node(1) print(w) # ListNode { head = 1,next = None } remove(w,1) print(w) # ??? 作为结果,但如果没有以下至少一项,这是不可能的 -

  1. 函数调用必须改变
  2. 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 + "∅" ,这是理所当然的。链表是一种递归数据结构,选择一个递归程序来处理它可以使我们的程序结构与我们的数据结构协调一致。然而,递归是一种函数式遗产,因此将它与函数式风格结合使用会产生最好的结果。这意味着避免诸如突变、变量重新分配和其他副作用之类的事情。不是破坏现有值,而是创建一个值 -

  1. 如果输入列表 recursion 为空,则已达到基本情况。返回空列表
  2. (归纳)输入为空;它至少有一个元素。如果第一个元素 t 与要移除的值 t.head 匹配,则返回子问题的结果 val
  3. (归纳)输入不为空并且第一个元素匹配要删除的值。构造一个新的列表节点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 举报,一经查实,本站将立刻删除。