【问题标题】:Linked lists and recursion in pythonpython中的链表和递归
【发布时间】:2018-05-09 16:24:16
【问题描述】:

我是 Python 编程新手。我的问题是关于链表的,我为链表编写了一个类,我需要做的是有一个函数,其输入作为指向列表头部的参考。 'linked_list.head' 据我了解,linked_list 是相关列表的名称。特别是使用递归,我试图找到列表的长度作为这个函数的输出。这是我的代码,我不太明白在这种情况下如何移动到下一个节点并返回具有递归的节点数。

import re
def special_match(strg, search=re.compile(r'[^A-Za-z.]').search):
    return not bool(search(strg))

class node:
    def __init__(self, data, next):
        self.data = data
        self.next = next

    def get_data(self):
        return self.data

    def set_data(self,value):
        self.data = value

    def get_next_node(self):
        return self.next

    def set_next_node(self,val):
        self.next = val

class linked_list:

    def __init__(self):
        self.head = None
        self.tail = None
        self.size = 0

    def add_first(self,e):
        newest = node(e,None)
        newest.next = self.head
        self.head = newest
        self.size = self.size+1
        if self.size == 1:
            self.tail = newest

    def add_last(self,e):
        newest = node(e,None)
        if self.size > 0:
            self.tail.next = newest
        else:
            self.head = newest
        self.tail = newest
        self.size = self.size+1

    def remove_first(self):
        if self.size == 0:
            print('The linked list is empty')
        elif self.size == 1:
            answer = self.head.data
            self.head = None
            self.tail = None
            self.size -= 1
            return answer
        else:
            answer = self.head.data
            self.head = self.head.next
            self.size = self.size - 1
            return answer

    def remove_last(self):
        if self.size == 0:
            print('The linked list is empty')
        elif self.size == 1:
            answer = self.tail.data
            self.head = None
            self.tail = None
            self.size -= 1
            return answer
        else:
            temp  = self.head
            while(temp.next is not None):
                temp = temp.next
            temp.next = None


    def node_number(self,reference):
        reference = str(reference)
        count = 0
        temp = self.head
        if special_match(reference) == True:
            count =+ 1
            temp = temp.next
            return self.node_number  
        else:
            print('You have made wrong input')

    def printe(self):
        curr = self.head
        while curr:
            print(curr.data)
            curr = curr.get_next_node()
        if self.size == 0:
            print('The list is empty')

【问题讨论】:

  • 让这家伙休息一下。他是一个新手,他只是在问一个问题(一个非常微不足道的问题,但它是特定于他的用例的。
  • 与其检查当前节点的真实性,不如检查下一个节点的存在。因此,如果 node.next != None,则进入下一个节点。
  • 您不需要任何 getter 或 setter。在python中,类和实例属性都是公开的。如果您想建议人们不要弄乱某些类或实例属性,请给他们起一个类似 self._next 的名称,其中 _ 表示它更多地供内部使用(但仍可公开访问)。然后,如果您想为该属性提供官方交互,请设置property decorated function
  • @user2357112 SO 也不鼓励在 cmets 中发布指向 google 的链接。如果您在场外发现可以回答问题的内容,请在答案中发布链接以及相关位的简短摘要。如果关于 SO 的另一个问题已经问过这个问题,则标记为重复。发表评论告诉 OP 到谷歌它不会帮助 OP 或其他任何后来发现这个问题的人寻求帮助。 SO在这里为您提供帮助。不要成为“家庭作业”警察。
  • @user2357112 你建议用谷歌搜索问题的标题+“长度算法”这个词。我只是不明白这对任何人有什么帮助。即使它确实提供了有用的结果,也请选择一个,在答案中进行总结并发布链接。这样每个人都会受益。

标签: python recursion linked-list


【解决方案1】:

递归是一种函数式遗产,因此将其与函数式风格一起使用将产生最佳效果。在您的程序中,您已经使用命令式mutable 节点实现了一个链表——也就是说,datanext 的值可以随时间变化。虽然这可能感觉是一种直观的方法,但我想专注于一个 不可变 实现,它将我们从严重的状态复杂性中解放出来。在这个答案中,我们将使用以函数样式表示的递归形式来实现 所有 链表函数。

我们从简单的nodelinked_list 类开始。这次我们跳过创建get_*set_* 函数,就像你所做的那样。在 Python 中还有其他方法可以做这种事情,我们稍后会看到

class node:
  def __init__ (self, left, right):
    self.left = left
    self.right = right


class linked_list:
  def __init__ (self, node = None):
    self.node = node

接下来,我们为列表定义原语propertiesis_emptyheadtail

class linked_list:
  def __init__ (self, node = None):
    self.node = node

  @property
  def is_empty (self):
    return self.node is None

  @property
  def head (self):
    if self.is_empty:
      raise Exception ("cannot get head of an empty list")
    else:
      return self.node.left

  @property
  def tail (self):
    if self.is_empty:
      raise Exception ("cannot get tail of an empty list")
    else:
      return self.node.right

现在node 的使用已经完全抽象,我们可以使用我们的新属性编写更高级别的列表行为

class linked_list:
  ... 

  def length (self):
    if self.is_empty:
      return 0
    else:
      return 1 + self.tail.length ()

在上面,我们看到通过使用它的属性来谈论我们的列表非常容易。在进一步讨论之前,让我们看看如何使用print 构建列表并将它们可视化。对于对象到字符串的转换,我们使用__str__

class linked_list:
  ... 

  def add_first (self, x):
    return linked_list (node (x, self))

  def __str__ (self):
    if self.is_empty:
      return "None"
    else:
      return str (self.head) + " -> " + str (self.tail)

ls = linked_list().add_first(3).add_first(2).add_first(1)
print (ls)
# 1 -> 2 -> 3 -> None

print (ls.length ())
# 3

记住,因为我们已经构建了一个不可变的链表,add_first 不会改变它被调用的链表

ls = linked_list().add_first(3).add_first(2).add_first(1)
print (ls)
# 1 -> 2 -> 3 -> None

print (ls.add_first (0))
# 0 -> 1 -> 2 -> 3 -> None

print (ls)
# 1 -> 2 -> 3 -> None

在我们继续之前,让我们更容易地构造我们的链表。我们添加了一个 static build 函数,它允许我们构建一个包含不同数量输入的列表

class linked_list:
  ...

  @staticmethod
  def build (x = None, *rest):
    if x is None:
      return linked_list ()
    else:
      return linked_list (node (x, linked_list.build (*rest)))

print (linked_list.build (1,2,3))
# 1 -> 2 -> 3 -> None

现在,让我们看看你的 remove_firstremove_last 函数

class linked_list:
  ...

  def remove_first (self):
    if self.is_empty:
      raise Exception ("cannot remove first element of an empty list")
    else:
      return self.tail

  def remove_last (self):
    if self.is_empty:
      raise Exception ("cannot remove last element of an empty list")
    elif self.tail.is_empty:
      return self.tail
    else:
      return linked_list (node (self.head, self.tail.remove_last ()))

ls = linked_list.build (1,2,3)
print (ls)
# 1 -> 2 -> 3 -> None

print (ls.remove_first ())
# 2 -> 3 -> None

print (ls.remove_last ())
# 1 -> 2 -> None

print (ls)
# 1 -> 2 -> 3 -> None

还有node_number

class linked_list:
  ...

  def node_number (self, index = 0):
    if self.is_empty:
      raise Exception ("index out of bounds")
    elif index is 0:
      return self.head
    else:
      return self.tail.node_number (index - 1)

ls = linked_list.build ("a", "b", "c")

print (ls.node_number (0))
# "a"

print (ls.node_number (1))
# "b"

print (ls.node_number (10))
# Exception: index out of bounds

还有一个add_last免费赠品

class linked_list:
  ...

  def add_last (self, x):
    if self.is_empty:
      return self.add_first (x)
    else:
      return linked_list (node (self.head, self.tail.add_last (x)))

ls = linked_list.build (1, 2, 3)
print (ls)
# 1 -> 2 -> 3 -> None

print (ls.add_last (4))
# 1 -> 2 -> 3 -> 4 -> None

print (ls)
# 1 -> 2 -> 3 -> None

repl.it的完整程序演示

【讨论】:

  • 感谢您的全面回答,据我了解这是双向链表?我很感激它非常有帮助的努力
  • 如果是双向链表,每个节点都可以得到下一个上一个节点。这只是一个单链表,因为每个节点 only 都有到下一个节点的链接。
【解决方案2】:

我会将它设置为长度函数实际上是Node 类而不是Linked_List 类的一部分。 Linked_List 类也会有一个length 函数,但它所要做的就是调用列表的head 节点的length 函数。

然后,每个节点将返回 lengthnext 实例加上 1。

【讨论】:

    【解决方案3】:

    递归应该有一个基本情况,代码检查next 属性是否为None。如果是这样,该函数返回当前计数。如果不是,则计数器递增,函数length 被调用为next 属性的方法,以便能够继续沿链接进行递归,可以写为:

    |val1|pointer| -> |val2|pointer| -> |val3|pointer| -> |val4|pointer| -> |val5|None|
    

    首先,下面是一个更简单的链表类构造用于演示:

    class Node:
       def __init__(self, val=None):
          self.head = val
          self.next = None 
       def length(self, count = 0):
          if self.next is None:
             return count + 1 if self.next is None and self.head else count
          return self.next.length(count + 1)
       def insert(self, v):
          if self.head is None:
             self.head = v
          else:
             if self.next is None:
               self.next = Node(v)
             else:
               self.next.insert(v)
       @classmethod
       def regular_transform(cls, node, nodes = []):
          '''for easier visulization'''
          return nodes+[node.head] if not node.next else cls.regular_transform(node.next, nodes+[node.head])
    
    n = Node()
    for i in [56, 232, 424, 2, 11]:
      n.insert(i)
    print(Node.regular_transform(n))
    print(n.length())
    

    输出:

    [56, 232, 424, 2, 11]
    5
    

    【讨论】:

    • 部分是因为长度函数doesn't handle false elements,部分是因为这给提问者做了太多的功课。
    • (就此而言,插入例程也不能正确处理错误元素。)
    • @user2357112 False 元素超出了示例的范围,这是为了演示递归长度查找方法,但我确实添加了更强大的检查。但是,没有办法证明 OP 的代码是作业。仅仅因为它是一个基本的 CS 结构并不意味着他没有自己编写代码,或者从在线资源中获取代码进行学习。
    猜你喜欢
    • 2018-03-30
    • 2011-05-04
    • 2022-12-02
    • 2021-03-25
    • 2015-05-06
    • 2021-12-22
    • 2014-04-08
    • 2017-04-21
    相关资源
    最近更新 更多