【问题标题】:Return middle node of linked list with recursion递归返回链表的中间节点
【发布时间】:2020-09-09 13:41:38
【问题描述】:

图是由节点和边组成的非线性数据结构。节点有时也称为顶点,边是连接图中任意两个节点的线或弧。更正式的 Graph 可以定义为

【问题讨论】:

  • 该函数只是返回废话或给我一个错误如果你向我们展示这些东西会有所帮助,而不是基本上只是说“发生了坏事”。
  • 我对你的代码有点困惑。您正在计算列表的长度并检查您是否同时处于中间点? ret_count 什么时候会等于 (self.len-1)/2
  • 请显示更多代码,但据我所知,当您执行self.find_mid(node.next, ret_count + 1) 时,您不会将返回的值放在某处并返回它。

标签: python recursion linked-list return


【解决方案1】:

打印正确值但将打印更改为 return 语句不起作用的原因是因为您没有在基本情况下返回节点。因此,当您找到中点并返回节点时,前一个节点不会返回任何内容或利用递归步骤的结果。这是一个修改,它将利用递归步骤的结果并将其返回到调用链中。

我并不完全相信你的中点计算在每种情况下都是正确的(3 个节点的情况将返回节点 1 而不是节点 2)所以我稍微修改了它。

def find_mid(self, node, ret_count, ):
    if node.next == None:
        self.len += 1
        return None
    else:
        self.len += 1
        # return_node will either be the midpoint if we have found it, or None if we are still searching
        return_node = self.find_mid(node.next, ret_count + 1)

        # We have found the midpoint no need to check ret_count anymore
        if return_node:
            return return_node

    # If we make it here we have not found the midpoint node but have counted the total number of Nodes.
    # Set midpoint assuming an even number of nodes
    midpoint = int(self.len/2)
    # If odd number of nodes set midpoint accordingly
    if self.len % 2 != 0:
        midpoint = int((self.len+1)/2)

    # Check if the current node is the midpoint (len = 3 or len = 4 results in node 2 being midpoint
    if ret_count == midpoint:
        # Return the node
        return node
    else:
        # Potentially not necessary but will ensure that non-midpoint recursive steps return None
        return None

【讨论】:

    【解决方案2】:

    对于链表进行操作,递归是一个糟糕的选择。几乎总是使用循环,这很容易推理,开销较小,并且不会将列表的大小限制为调用堆栈。迭代访问和操作周围的元素更容易。

    迭代获取链表的中点很容易:保持对头部的两个引用,然后以另一个速度两倍的速度移动一个,直到快速引用到达链表的末尾。慢速指针将指向中间节点。两指针技术是处理链表的基本工具之一。

    from collections import namedtuple
    
    def middle_node(fast):
        slow = fast
    
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
    
        return slow
    
    if __name__ == "__main__":
        Node = namedtuple("Node", "val next")
        odd = Node(0, Node(1, Node(2, Node(3, Node(4, None)))))
        even = Node(0, Node(1, Node(2, Node(3, Node(4, Node(5, None))))))
        print(middle_node(odd).val) # => 2
        print(middle_node(even).val) # => 3
    

    您可以使用完全相同的方法递归地执行此操作:快速引用和慢速引用。这是插入上述样板的示例:

    def middle_node(fast, slow=None):
        if not slow:
            slow = fast
    
        if fast and fast.next:
            return middle_node(fast.next.next, slow.next)
    
        return slow
    

    对于具有两个中间元素的奇数长度列表,这些方法总是返回离尾部更近的一个,但您可以在基本情况或循环终止条件下添加一个额外的fast.next.next 检查以返回更靠近尾部的中间元素如果需要,请前往。

    您可以看到递归版本在可读性或优雅方面没有任何好处,以证明它所施加的额外开销和大小限制是合理的。递归更适合非线性嵌套结构,例如数据很宽的树(这意味着调用堆栈更能保存数据而不会溢出)。树的非线性特性使得使用循环和显式堆栈来处理某些典型的遍历非常尴尬。

    【讨论】:

      【解决方案3】:

      递归是处理链表的绝佳选择,因为链表是递归数据结构。递归允许我们的程序结构与我们的数据结构相匹配 -

      # linked_list.py
      
      empty = None
      
      class node:
        def __init__(self, value, next = None):
          self.value = value
          self.next = next
      
      def to_str(t = empty):
        if not t:
          return "None"
        else:
          return f"{t.value} -> {to_str(t.next)}"
      
      def middle(t = empty):
        def loop(t, ff):
          if not t:
            return None
          elif ff and ff.next:
            return loop(t.next, ff.next.next)
          else:
            return t.value
        return loop(t, t)
      

      让我们从我们的基本构建块中获得一些输出 -

      # main.py
      
      from linked_list import node, to_str, middle
      
      t1 = node(1, node(2, node(3)))
      t2 = node(1, node(2, node(3, node(4))))
      t3 = node(1, node(2, node(3, node(4, node(5)))))
      
      print(to_str(t1)) # 1 -> 2 -> 3 -> None
      print(to_str(t2)) # 1 -> 2 -> 3 -> 4 -> None
      print(to_str(t3)) # 1 -> 2 -> 3 -> 4 -> 5 -> None
      
      print(middle(t1)) # => 2
      print(middle(t2)) # => 3
      print(middle(t3)) # => 3
      

      将功能模块包裹在 class 中会让人感觉更像 Python -

      # linked_list.py
      
      empty = # ...
      
      class node # ...
      
      def to_str # ...
      
      def middle # ...
      
      def from_list(l = []):
        if not l:
          return empty
        else:
          return node(l[0], from_list(l[1:]))
      
      class linked_list:
        def __init__(self, root = None):
          self.root = root
        def __str__(self):
          return to_str(self.root)
        def middle(self):
          return middle(self.root)
        def from_list(l = []):
          return linked_list(from_list(l))
      

      现在我们获得了函数式模块的所有好处以及 oop 式接口的便利 -

      from linked_list import linked_list
      
      t1 = linked_list.from_list([1, 2, 3])
      t2 = linked_list.from_list([1, 2, 3, 4])
      t3 = linked_list.from_list([1, 2, 3, 4, 5])
      
      print(t1) # 1 -> 2 -> 3 -> None
      print(t2) # 1 -> 2 -> 3 -> 4 -> None
      print(t3) # 1 -> 2 -> 3 -> 4 -> 5 -> None
      
      print(t1.middle()) # => 2
      print(t2.middle()) # => 3
      print(t3.middle()) # => 3
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-15
        • 1970-01-01
        • 1970-01-01
        • 2018-12-31
        • 2010-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多