簡體   English   中英

反向鏈接列表遞歸不使用任何新變量

[英]Reverse Linked List Recursively without using any new variable

在求職面試中,我被要求在C中編寫一個遞歸反轉鏈表的函數,返回新的第一個節點,並且不使用任何新節點。

你怎么能這樣做?

這是一般概念,使用類似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'的答案),否則無法消除這兩個並有一個工作算法。 更好地發揮其安全性並使用臨時變量來執行評估順序並始終獲得正確的結果。

這很簡單:遞歸到列表的末尾,每次傳入新的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);

我不知道什么是“遞歸鏈表”,所以我會使用你的標題並假設你想要使用遞歸函數“反轉鏈表”。

我假設它是一個單獨的鏈表(因此每個元素next一個元素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);
}

嘿伙計們請在下面找到帶有和不帶遞歸的反向鏈表的程序

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;
}

因此, ÓscarLópez在這里的答案的變化是

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

表達式(a,b)的序列返回其最后一個值,並從左到右進行計算。

這僅適用於保證函數參數的評估順序為從左到右的編譯器。 如果是從右到左 ,則應該交換invert的參數。

如果評估順序是不確定的 ,那么這將不起作用。 C標准顯然沒有指定這個順序,但是一些編譯器可能有自己的方式。 您可能知道有關您的編譯器的信息,但它是不可移植的,您的編譯器也可能在將來更改它。 寫這樣的代碼是不受歡迎的。 也許這就是你的采訪者想要聽到的。 他們喜歡這樣的技巧,並希望看到你的反應。

控制評估順序的常規方法是使用臨時變量。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM