反向链接列表递归不使用任何新变量

时间:2012-10-03 01:13:55

标签: c recursion linked-list reverse

在求职面试中,我被要求在C中编写一个递归反转链表的函数,返回新的第一个节点,并且不使用任何新节点。

你怎么能这样做?

5 个答案:

答案 0 :(得分:3)

这是一般概念,使用类似C语法的不可知语言:

Node invert(Node list, Node acc) {
    if (list == null)
        return acc;
    Node next = list.next;
    list.next = acc;
    return invert(next, list);
}

上述函数接收要反转的列表的第一个节点和到目前为止的累加值,并返回新反转列表的头部 - 节点的反转是就地完成的,没有分配额外的空间(除了堆栈中的局部变量)。这样称呼:

invert(firstNodeOfList, null);

这是尾递归的一个例子:结果收集在acc参数中,当每个递归调用返回时,没有什么可做的,只返回累计值。

<强>更新

在函数式语言中,编写函数来反转列表而不使用局部变量更容易,更自然,例如在Scheme中查看以下代码 - 它有缺点,它会在调用时创建一个新的列表节点cons程序:

(define (invert lst acc)
  (if (empty? lst)
      acc
      (invert (rest lst)
              (cons (first lst) acc))))

(invert '(1 2 3 4 5) '())
> '(5 4 3 2 1)

底线:您可以在每次递归调用时创建一个新节点或创建一个新的局部变量,但除非编程语言提供顺序执行的表达式,并且编译器保证评估顺序(请参阅@WillNess的答案),您可以' t消除两者并具有工作算法。更好地发挥其安全性并使用临时变量来强制执行评估顺序并始终获得正确的结果。

答案 1 :(得分:3)

这很简单:递归到列表的末尾,每次传入新的next指针并返回列表的末尾。

struct list {
    struct list *next;
};

struct list *reverse(struct list *cur, struct list *prev) {
    struct list *next = cur->next;
    cur->next = prev;
    if(next == NULL) return cur;
    return reverse(next, cur);
}

reverse(head, NULL);

答案 2 :(得分:2)

我不知道“递归链接列表”是什么,所以我会使用你的标题并假设你想要使用递归函数“反转链表”。

我假设它是一个单链表(因此每个元素都有一个指针next到下一个元素),按照惯例,next = NULL表示最后一个元素。 对于双向链表,你还必须处理prev指针,但它基本上是相同的想法。使用双向链表更简单,因为你甚至不需要跟踪前一个元素;你只需要在每个元素上翻转两个指针。

要反转列表,我们可以想象一次向下移动一个元素,在我们去的时候翻转next指针指向前一个元素。这很容易编写为递归函数,它将参数指向当前元素和指向前一个元素的指针(在开始时该指针为NULL)。

node* reverseList(node* head, node* prev = NULL) {
  if (head == NULL) return prev;

  std::swap(head->next, prev); // prev points to next element to process
  return reverseList(prev, head);
}

答案 3 :(得分:1)

嘿,伙计们请在

下找到反向链接列表的程序,包括和不带递归
LINK *reverse_linked_list_recursion(LINK *head)
{
        LINK *temp;
        if (!head) {
                printf("Empty list\n");
                return head;
        }
        else if (!head->next)
                return head;
        temp = reverse_linked_list_recursion(head->next);
        head->next->next = head;
        head->next = NULL;
        return temp;
}

LINK *reverse_linked_list_without_recursion(LINK *head)
{
        LINK *new, *temp;
        if (!head) {
                printf("No element in the list\n");
                return head;
        }
        else {
                new = head;
                while (head->next) {
                        temp = head->next;
                        head->next = temp->next;
                        temp->next = new;
                        new = temp;
                }
        }
        return new;
}

答案 4 :(得分:0)

因此,Óscar López's answer here的变体是

Node invert(Node list, Node acc) 
{
    if (!list) 
        return acc; 
    return invert(list.next, ((list.next=acc),list));
}

表达式序列(a,b)返回其最后一个值,并从左到右进行评估。

对于保证函数参数的评估顺序为从左到右的编译器,这只会起作用 。如果从右向左,则应交换invert的参数。

如果评估订单是 nondeterministic ,则无效。 C标准显然没有指定这个顺序,但是一些编译器可能有自己的方式。您可能知道有关您的编译器的信息,但它是不可移植的,您的编译器也可能在将来更改它。写这样的代码是不受欢迎的。也许这就是你的采访者想要听到的。他们喜欢这样的技巧,并希望看到你的回应。

控制评估顺序的常用方法是使用临时变量。