【问题标题】:Recursion without parameters, nor static or global variables没有参数的递归,也没有静态或全局变量
【发布时间】:2019-08-25 03:04:24
【问题描述】:

我得到了一个作业,要求我遍历一个带有给定类头的链表,我不应该更改它:

template<typename ItemType>
class LinkedList{
public: 
...
  LinkedList();
  LinkedList(const LinkedList<ItemType>&);
  int getCurrentSize340RecursiveNoHelper() const; 

private:
  Node<ItemType>* headPtr{ nullptr }; // Pointer to first node
}

节点类头是:

template<typename ItemType>
class Node {
public:
...
  Node();
  Node(const ItemType&);
  Node(const ItemType&, Node<ItemType>*);
  Node<ItemType>* getNext() const;

private:
  ItemType        item;          // A data item
  Node<ItemType>* next{ nullptr }; // Pointer to next node
}

在函数getCurrentSize340RecursiveNoHelper()中,我们应该迭代链表以获取大小。

我知道我可以在静态或全局的帮助下迭代链表,但我的教授说我们应该避免使用它们。有没有办法做到这一点?

【问题讨论】:

  • 函数要做什么?
  • @0x499602D2 我猜它必须只返回链表的大小? getCurrentSize340RecursiveNoHelper
  • 递归获取链表的大小。
  • 这里提示:链表的大小是链表大小的1+,头部在下一个元素
  • 如何检查列表是否包含 0 个元素?一个?

标签: c++ recursion linked-list


【解决方案1】:

您可以通过成员变量而不是参数进行递归。

int LinkedList::getCurrentSize340RecursiveNoHelper() const {
    if (this->headPtr == nullptr){
        return 0;
    }

    Node<ItemType> nextNode = this->headPtr->getNext();
    if (nextNode != nullptr){
        LinkedList<ItemType> restOfList;
        restOfList.add(nextNode); // a bit weird but this is the only way you can set the headPtr of a linked list.
        return 1 + restOfList.getCurrentSize340RecursiveNoHelper();
    }

    return 1;
}

【讨论】:

  • 你忘记了基本情况。
  • @0x499602D2 已更新。然而,我认为这个特定的函数保证了至少 1 的大小。这是因为因为它是一个成员函数,如果你在一个不为空的节点上调用它,这意味着该列表在链表中至少有一个元素——它本身。
  • 这是真的,除非它充当哨兵节点。
  • 另外请注意,您可以在大型列表上一直递归到 StackOverflow。
【解决方案2】:

在函数getCurrentSize340RecursiveNoHelper()中,我们假设 迭代链表以获取大小。

我知道我可以在 static 或 全球性的,但我的教授说我们应该避免使用它们。

有没有办法做到这一点?

最后一个问题:是的,这是可能的,甚至很容易。软件非常灵活。

没有函数参数的递归(也不是静态变量,也不是全局变量)。

迭代和递归没有参数都很容易做到,一旦你看到了。

通过练习,您可以轻松避免静态变量和全局变量。

另请参阅:https://stackoverflow.com/a/45550373/2785528,它提供了一种简单的机制来“在不使用全局变量的情况下通过系统传递用户输入”。我们可以将其总结为 2 个步骤,a)在 main 早期实例化一个自定义类实例以包含(用于“传输”)任何用户输入/值,否则您将放置在全局变量中以供各种用户获取,然后 b)通过引用需要它们的对象的ctor来传递这些传输对象(类似于旅行箱)。很简单。


感谢教授坚持使用用户定义类型 (UDT)。这是你需要练习的东西。

但是,对于这篇文章,通过忽略您声明没有问题的 UDT 来研究操作方法(没有参数的迭代和递归等)会更简单。这样,我们就可以专注于函数形式。

为了进一步简化,我的示例将是 Functors ... 一种简短而简单的类形式。我推荐 Functor 类既简单又高效。

还有一个简化,我将使用 std::list。有很多关于如何制作、填充和使用这个容器的例子。

一个关键思想(支持无参数函数)是将数据和方法“捆绑”到类中。封装是您的朋友。


想法 1 -- 最小化 main()。保持简短。出去直奔业务。

int main(int, char**)
{
   int retVal = 0;
   retVal    += F820_Iterative_t()();
   retVal    += F820_Recursive_t()();
   return retVal;
}

这里调用了两个函子。我将迭代与递归示例分开。

请注意,这些函子是在 main 的早期调用的。有很多东西在 main 之前被初始化(但请参阅众所周知的初始化惨败)。这简化并控制了这些初始化发生的时间。

第一个函子将实例化、处理、然后析构,在下一个函子开始之前完成其整个生命周期,即它们被序列化。


数据类型:

// examples use std::list
// user typedefs ---------------vvvvvvvvvvv
typedef list<string>            StrList_t;
typedef list<string>::iterator  StrListIt_t;

数据属性:

// Functor
class F820_Iterative_t
{
   // NOTE: class instance data attributes are not global vars
   StrList_t    m_strList; // std::list<std::string>
   StrListIt_t  m_it;      // std::list<std::string>::iterator
   ...


// Functor
class F820_Recursive_t
{
   StrList_t    m_strList; // std::list<std::string>
   StrListIt_t  m_it;      // std::list<std::string>::iterator
   ...

示例 1 -- 迭代元素计数

// iterate with no parameters, static vars, or global vars
uint F820_Iterative_t::iterateCount( )
   {                           // --^-- no params
      uint lcount = 0;
      do // -------------iterative loop
      {
         if (m_it == m_strList.end()) // detect end of list
            break;                    // kick out

         lcount += 1;

         m_it++;                   // step to next element

      } while(true);

      return(lcount);
   }

示例 2 -- 递归元素计数

// recurse with no parameters, static vars, or global vars
uint F820_Recursion_t::recurseCount( )
   {                           // --^-- no params
    if (m_it == m_strList.end()) // RTC (recursion termination clause)
        return 0;

    m_it++; // step to next element

    return (1 + recurseCount());   // tail recursion
} //          --^^^^^^^^^^^^-- recursive invocation

在上述两个元素计数函数中,Functor 数据属性在 F820_xxx::xxxCount() 被调用之前被初始化。

此代码不使用函数参数,也不使用全局变量。这些函数(m_it 和 m_strList)中使用的变量是类实例的数据属性。该函数通过隐含的“this”指针直接访问它们。


生活中断。

上面两个 xxxxCount() 用于比较。如果需要,请询问更多,此代码将编译并运行。我打算找时间插入其余部分。

【讨论】:

    【解决方案3】:

    当您处理递归问题时,一个技巧是假设递归函数在当且仅当您给它一个比您正在处理的问题更简单的问题时才能工作。你会得到一个指向链表头部的指针。处理完基本情况后,您现在知道列表有至少一个元素。一个更简单的问题是你可以让你的 recusive 函数来计算列表的 rest 的长度,然后将结果加 1。

    【讨论】:

      猜你喜欢
      • 2020-06-18
      • 2017-10-03
      • 1970-01-01
      • 2014-04-24
      • 2014-04-02
      • 1970-01-01
      • 2012-04-29
      • 1970-01-01
      • 2021-11-13
      相关资源
      最近更新 更多