【问题标题】:How to write this recursion with loops如何使用循环编写此递归
【发布时间】:2019-11-07 22:14:25
【问题描述】:

我在网站上看到以下内容作为练习。它基本上说在不使用递归和不使用向量、堆栈等结构的情况下编写以下函数:

void rec(int n) {
        if (n != 0) {
                cout << n << " ";
                rec(n-1);
                rec(n-1);
        }
}

起初我以为这很容易,但我却出人意料地努力完成它。

为了更好地理解它,我将它定义为一个数学函数,如下所示:

f(x) = {1 if x = 0, f(x-1) + f(x-1) else}(其中 + 运算符表示连接,- 是正常的减号)

但是,展开这个变得更加困难,我被困住了。有没有直接的方法把它写成循环?而且,更一般地说,是否有解决此类问题的算法?

【问题讨论】:

  • 请记住,递归只是利用堆栈为您工作的一种方式。总有一种等效的方法,您可以使用循环来管理自己的堆栈。
  • @tadman 我知道,当时我在学习时做了很多这样的事情,但我认为有一个非常简单的方法,我看不到。通常展开递归总是对我有用。
  • 问题是这两个路径分支,然后这两个分支也是如此。也许您可以考虑它给出的输出,然后找到这些数字的模式并在此基础上实施。
  • @tadman 正如我所说,我展开了递归并看到了模式,但我不知道如何使用循环轻松实现它。我知道肯定有一种方法,因为递归和迭代具有与您所说的相同的“力量”。
  • 这个怎么样? Attempt on coliru ;-) 回忆已经完成的输出是否超出了您的限制? (恐怕是这样。实际上,是 std::ostringstream 被“滥用”为堆栈。)

标签: c++ algorithm recursion


【解决方案1】:

如果你玩够了,你至少可以得到一种输出有序序列而无需重新访问它的方法:)

let n = 5

// Recursive 
let rec_str = ''
function rec(n) { 
  if (n != 0) { 
    rec_str += n
    rec(n-1); 
    rec(n-1); 
  } 
}

rec(n)
console.log(rec_str)

// Iterative 
function f(n){
  let str = ''
  
  for (let i=1; i<1<<n; i++){
    let t = i
    let p = n
    let k = (1 << n) - 1

    while (k > 2){
      if (t < 2){
        break 
      } else if (t <= k){
        t = t - 1
        p = p - 1
        k = k >> 1
      } else {
        t = t - k
      }
    }
    str += p
  }
  console.log(str)
}

f(n)

(代码正在构建一个字符串,我认为按照规则应该不允许,但仅用于演示;我们可以直接输出数字。)

【讨论】:

  • 问题被标记为c++。这肯定是非法的c++ 语法。
  • @Fureeish 让我休息一下,这个问题也被标记为“递归”和“算法”(尽管后者是我的编辑 :)
  • @Fureeish 如果您需要帮助将此代码转换为 C++,请告诉我。 (要获得预期的类似结果,str += p 将是 cout 调用。)
  • 我相信我自己可以将基本的 JS 代码转换为 C++,谢谢,但这不是重点。该问题被标记为c++,我相信答案应该在c++ 中,无论该主题是否涉及任何其他与语言无关的领域。在我看来,如果语言无关紧要,那么问题就不会被标记为特定语言。
  • @Fureeish 如果 C++ 中的代码需要更重要的特定于语言的调用和过程,我会同意。在这种情况下我喜欢 JS,因为它很容易添加可点击的 sn-p。对于这个特殊的例程,我认为代码足够接近 C 来做出妥协。
【解决方案2】:

void loop(int n)
{
    int j = 0;
    int m = n - 1;

    for (int i = 0; i < int(pow(2, n)) - 1; i++)
    {
        j = i;
        if (j == 0)
        {
            std::cout << n << " ";
            continue;
        }
        m = n - 1;
        while (true)
        {
            if (m == 1)
            {
                std::cout << m << " ";
                m = n - 1;
                break;
            }
            if (j >= int(pow(2, m)))
            {
                j = j - int(pow(2, m)) + 1;
            }
            if (j == 1)
            {
                std::cout << m << " ";
                m = n - 1;
                break;
            }
            else
            {
                j--;
            }
            m--;
        }
    }
    std::cout << std::endl;
}

例如 n = 3

out =     [3 2 1 1 2 1 1] 
indexes = [0 1 2 3 4 5 6] 

考虑索引列表;对于 i > 0 和 i

例如,当 n = 4 时,这就是所有索引在每个 while 步骤中发生的情况。 p(x) 表示值 x 在该索引处打印。 A / 表示索引已经被打印出来了。:

n = 4,m = 3
[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]
m = 3
[p(n=4) 1 2 3 4 5 6 7 8 9 10 11 12 13 14]
if(i >=2^3) -> i = i -2^3 + 1)
[/ 1 2 3 4 5 6 7 1 2 3 4 5 6 7]
if(i == 1) -> print m, else i = i -1
[/ p(3) 1 2 3 4 5 6 p(3)1 2 3 4 5 6]

m = 2
if (i >=2^2) -> i = i - 2^2 +1
[/ / 1 2 3 1 2 3 / 1 2 3 1 2 3] 
if(i == 1) -> print m, else i = i -1
[ / / p(2) 1 2 p(2) 1 2 / p(2) 1 2 p(2) 1 2]
m = 1
if (m == 1) -> print(m)
[ / / / p(1) p(1) / p(1) p(1) / / p(1) p(1) / p(1) p(1)]

因此结果是:

[4 3 2 1 1 2 1 1 3 2 1 1 2 1 1]

【讨论】:

  • 有趣的观察!这也比我的想法更有效(它似乎减少了迭代,但我认为在一个相似的数量级上)。
  • 谢谢!我添加了一个小例子,因为我担心它很难遵循。我在处理案件时遇到了一些麻烦
【解决方案3】:
void via_loop(int n) {
  string prev = "1 ", ans = "1 ";
  for (int i = 2; i <= n; i++) {
    ans = to_string(i) + " " + prev + prev;
    prev = ans;
  }
  cout << ans;
}

想法是保存每个数字先前计算的结果。完整代码:

void rec(int n) {
        if (n != 0) {
                cout << n << " ";
                rec(n-1);
                rec(n-1);
        }
}

void via_loop(int n) {
  string prev = "1 ", ans = "1 ";
  for (int i = 2; i <= n; i++) {
    ans = to_string(i) + " " + prev + prev;
    prev = ans;
  }
  cout << ans;
}

int main() {
  int n = 5;

  cout << "Rec : ";
  rec(n);
  cout << endl;

  cout << "Loop: ";
  via_loop(n);
  cout << endl;

}

输出:

Rec : 5 4 3 2 1 1 2 1 1 3 2 1 1 2 1 1 4 3 2 1 1 2 1 1 3 2 1 1 2 1 1
Loop: 5 4 3 2 1 1 2 1 1 3 2 1 1 2 1 1 4 3 2 1 1 2 1 1 3 2 1 1 2 1 1

【讨论】:

  • 这不能回答问题。 OP提供的示例按顺序输出序列。您无法重新访问该序列。构建字符串是一种特殊的结构,就像一个向量,OP 说这是不允许的。
猜你喜欢
  • 2021-01-18
  • 1970-01-01
  • 1970-01-01
  • 2011-07-16
  • 2023-04-03
  • 1970-01-01
  • 2018-06-24
  • 2013-07-24
  • 1970-01-01
相关资源
最近更新 更多