【问题标题】:How would I convert this recursive function to an iterative one?如何将此递归函数转换为迭代函数?
【发布时间】:2020-08-20 11:49:54
【问题描述】:

我在尝试将此递归函数 find_reachable(..) 转换为它的迭代等效项时遇到了麻烦。我环顾四周,看到了使用堆栈的建议,但无法弄清楚。我也认识到这个函数是尾递归的,但不知道如何处理这个信息。 if 声明让我特别难过。任何帮助表示赞赏,谢谢。

void find_reachable(struct person *current, int steps_remaining,
                              bool *reachable){
    // mark current root person as reachable
    reachable[person_get_index(current)] = true;
    // now deal with this person's acquaintances
    if (steps_remaining > 0){
        int num_known = person_get_num_known(current);
        for (int i = 0; i < num_known; i++){
            struct person *acquaintance = person_get_acquaintance(current, i);
            find_reachable(acquaintance, steps_remaining - 1, reachable);
        }
    }
}
.....
struct person {
  int person_index; // each person has an index 0..(#people-1)
  struct person ** known_people;
  int number_of_known_people;
};

// return the person's unique index between zero and #people-1
int person_get_index(struct person * p) {
  return p->person_index;
}

// return the number of people known by a person
int person_get_num_known(struct person * p) {
  return p->number_of_known_people;
}

// get the index'th person known by p
struct person * person_get_acquaintance(struct person * p, int index) {
  //fprintf(stderr, "index %d, num_known %d\n", index, p->number_of_known_people);
  assert( (index >= 0) && (index < p->number_of_known_people) );
  return p->known_people[index];
}


【问题讨论】:

  • 我们很难猜出person_get_index()person_get_num_known()person_get_acquaintance() 的功能到底在做什么。如果您不发布那部分代码,您将不会看到很多人可以帮助您。
  • @PierreFrançois 谢谢,我编辑了问题
  • 您可能想了解已转换为迭代循环的函数,例如,qsort
  • Fwiw,这不是尾递归;将尾递归转换为迭代不需要堆栈。

标签: c loops recursion iteration tail-recursion


【解决方案1】:

完全透明,我对c不太了解,所以在我不知道语法的地方使用了伪代码,但是这样的东西可能会起作用吗?

将递归转换为迭代的一般想法通常需要将需要处理的对象(在本例中为人)添加到堆栈中。您在某些条件下添加这些对象(在这种情况下,如果它们在熟人网络中不太深)。

然后,只要堆栈不为空,您就会进行迭代,弹出顶部元素并处理它。 “处理”步骤通常还包括向堆栈添加更多元素。

您实际上是在模仿递归函数产生的“调用堆栈”。

如果您关心元素的处理顺序,这将涉及更多内容,但在这种情况下,您似乎不需要这样做,因为它们仅被标记为“可达”。

void find_reachable(struct person *current, int steps_remaining, bool *reachable){

    stack = /* create a new stack which contains a (person, int) touple or struct */

    stack.push(/* current & steps_remaining*/)

    while (/*stack is not empty*/) {
        currentStruct = /* pop the top of stack */
        current = currentStruct.person
        depth = currentStruct.depth

        reachable[person_get_index(current)] = true;

        if (depth - 1 <= 0) {
            continue;
            // don't add this person's aquantances b/c we're 'steps_remaining' levels in
        }

        int num_known = person_get_num_known(current);
        for (int i = 0; i < num_known; i++){
            struct person *acquaintance = person_get_acquaintance(current, i);
            stack.add(/*acquantance & depth - 1*/)
        }
    }
}

编辑:改进的代码

【讨论】:

    【解决方案2】:

    这看起来像是一种深度优先搜索:它通过检查每个人的第一个熟人,然后检查那个熟人的第一个熟人,依此类推,最后回溯。递归通常是深度优先搜索的一个很好的策略,大多数迭代实现实际上使用堆栈来记录树中较高节点的地址,以便以后回溯到它们。迭代深度优先搜索是这样的:

    1. S 是一个堆栈,最初只包含正在搜索的图的根。
    2. 从栈中弹出到变量v中。
    3. v 的所有孩子(或在本例中称为“熟人”)推送给 S
    4. 如果堆栈为空,则终止;否则,请转到第 2 步。

    在 C 中实现堆栈的最简单方法是使用单链表,如下所示:

    struct person_stack;
    struct person_stack {
        struct person *who;
        struct person_stack *next;
    };
    
    struct person *person_stack_pop(struct person_stack **s) {
        struct person_stack *old_top = *s;
        struct person *who = old_top->who;
        *s = *s->next;
        free(old_top);
        return who;
    }
    
    struct person_stack *person_stack_push(struct person_stack **s, struct person *p) {
        struct person_stack *new_top = malloc(sizeof (struct person_stack));
        new_top->next = *s;
        new_top->who = p;
        *s = new_top;
        return *s;
    }
    

    不过,这里有一个并发症!您的函数仅搜索给定深度。这就是 if 语句首先存在的原因:当搜索足够深时终止递归。常规的 DFS 仅在没有要搜索的子项时才回溯,因此您必须添加一些额外的逻辑以使其知道其与根的距离。

    您可能还想确保仅当熟人尚未在堆栈中时才将其推入堆栈。这将使您免于重复迭代——想想如果这些人中的许多人有共同的熟人会发生什么。

    【讨论】:

      猜你喜欢
      • 2021-11-03
      • 2021-07-06
      • 2014-03-01
      • 2020-05-08
      • 2020-08-08
      • 2015-05-30
      相关资源
      最近更新 更多