單向鍊錶反轉,就地逆置與遞迴反轉(無表頭結點)

2022-07-23 22:54:23 字數 3431 閱讀 5379

最近在看鍊錶,今天刷到一道鍊錶的反轉題,鍊錶反轉可以說是基礎操作,但是可提供的方案也有很多,簡單通過了該題後又學習了一下遞迴反轉,現在把三種方法都公開出來做乙個總結。

1.就地逆置

2.單引數的遞迴逆置

3.雙引數的遞迴逆置

一、就地逆置

方法:頭插。

由於這裡是不帶表頭結點的單向鍊錶,所以頭插會稍微複雜一點,不想往下看的小夥伴也可以直接選擇定義乙個臨時表頭結點從頭結點開始遍歷鍊錶將每乙個煉表頭插,最後將頭結點指向表頭結點的next指標域,最後free掉那個表頭結點即可。

雖然不帶表頭結點頭插會稍微複雜一些,但是只要加一條語句就可以解決問題。

在不帶表頭結點的單向鍊錶中進行頭插,唯一要特殊處理的地方就是頭結點。而中間結點和尾結點的頭插可以簡化為下面的**段

pre->next  = head;

head = pre;

在找到普遍規律後,接下來就要考慮特殊情況是否能適應普遍規律。在這裡如果pre指向煉表頭結點,pre->next = head;則產生乙個環,導致的結果就是在鍊錶遍歷結束後,尾部會產生乙個長度為1的環。

而我們想要的尾部應該是指向null的,所以很顯然,if pre == head,  pre->next = null; 為了使**更加美觀具有統一性,所以我們增設乙個指標 q,初始值為null即可。

**如下

elemsn *reverselist(elemsn *head)

return

head;

}

------------------------以下為9.6號補充-----------------

上面這段就地逆置**本質上為建立鍊錶,下面給出同等效果的另一種形式。(這種形式將節省乙個指標p的空間,並用head來代替p遍歷鍊錶,用h1指標指向所建立新鏈的頭結點,這種形式更加突出的體現了以建鏈來逆置鍊錶的思想)

elemsn *reverselist(elemsn *head)

returnh1;

}

至於選用哪種方法依個人愛好即可,這裡只是貼出來發散一下思維。

---------------------------end---------------------------------

二、單引數遞迴逆置

如果單向鍊錶可以從後向前遍歷,那我想要逆置鍊錶不就變得容易的多,只需要從尾結點開始不斷的讓尾結點指向前乙個結點即可。

但事實上單向鍊錶只能單向遍歷,如果想要直接逆向遍歷是幾乎不可能的,但是遞迴間接的幫我們實現了這個功能。

先擺一下函式宣告

elemsn *reverselist(elemsn *head);

遞迴無非就是自己呼叫自己,理解遞迴其實很容易,在遞迴裡,reverselist函式每呼叫自己一次,都相當於將該函式全部**貼上到函式呼叫處一次,直到某次呼叫自己時觸發了return 返回,然後不斷的返回返回,直到返回到第一次呼叫,最終返回乙個結果值。

因此看乙個簡單的遞迴函式

elemsn *reverselist(elemsn *head)

elemsn *nextnode = head->next;

elemsn *reversenode = reverselist(nextnode);

return

reversenode;

}

該函式通過不斷呼叫自己實現對鍊錶的遍歷,並且該函式將head->next == null作為函式第一次的返回點,也就是當形參head到達尾結點時開始返回,之後每次返回都會將該結點傳至上一層呼叫,直至到達最頂層呼叫時返回尾結點。

可見遞迴,從最頂層開始逐級呼叫自己的過程是從頭到尾遍歷鍊錶的過程,而從最底層呼叫開始逐級返回則是乙個由尾到頭遍歷鍊錶的過程。而我們要做的逆置操作就從函式逐級返回開始,因此我們就可以利用這一特性寫出下面的遞迴逆置**

elemsn *reverselist(elemsn *head)

elemsn *nextnode = head->next;

head->next = null; //

很重要的一條語句哦,如果省略依舊會在新生成的鍊錶尾部產生乙個長度為1的環哦,可以注釋掉本條**執行再檢視一下輸出結果

elemsn *reversenode = reverselist(nextnode); //

遞迴遍歷鍊錶

nextnode->next = head; //

開始逐級返回開始後,尾變頭,原來的結點序中,後乙個結點依次指向前乙個結點

return reversenode; //

每次返回都帶回原尾結點(現在的頭結點)

}

三、雙引數遞迴逆置

從上乙個方法中了解到遞迴逆置無非就是在逐級返回這個過程中 不斷讓後乙個結點指向前乙個結點。

那我們顯然可以直接將前後結點作為函式形參在遞迴過程中依次遍歷鍊錶,當後結點到達尾結點時,開始逐級返回並讓後結點的next指向前結點,當然這裡需要記得在函式返回時傳遞尾結點(逆置後的頭結點)

**如下

elemsn *reverselist(elemsn *ptr, elemsn *pre)

elemsn *reversenode = reverselist(ptr->next, ptr);

ptr->next =pre;

return

reversenode;

}

在這個函式中不用擔心會在尾部生成乙個環,遞迴,從**開始,最終又會回到**,所以在呼叫該函式時 可以使用 head = reverselist(head, null);的語句,這樣子,函式的起點狀態將是 ,ptr = head, pre = null,最終也會是這個狀態,所以 在最後一次返回該函式時

ptr->next = pre;相當於我們傳進去的頭指標next指向了null,這樣逆置後的鍊錶尾部自然就是null了。

--------------------------------------以下為9.6號補充------------------------------

再次回顧該**時,發現函式的第一次返回點和函式逐級返回時的語句有重複的地方,再次思考後發現該重複點可以合併,於是就產生了下面的**。(兩處分別是 return ptr;和return reversenode;的上一條語句 ptr->next = pre;)

elemsn *reverselist(elemsn *ptr, elemsn *pre)

elemsn *reversenode = reverselist(ptr->next, ptr);

ptr->next =pre;

return

reversenode;

}

--------------------------------------------end-----------------------------------------

end。

單向迴圈鍊錶就地逆置

原來自己想過乙個思路,後來發現完全是不正確的。看來最後還是得在網上查詢演算法,最後才實現了。1 當鍊表為空表或只有乙個結點時,該鍊錶的逆置鍊錶與原表相同。2 當鍊表含2個以上結點時,可將該煉表處理成只含第一結點的帶頭結點鍊錶和乙個無頭結點的包含該鍊錶剩餘結點的鍊錶。然後,將該無頭結點鍊錶中的所有結點...

鍊錶就地逆置

就地逆置,就是在不借助任何中間變數的情況下,逆置一單鏈表。演算法思路 逆置後的點鍊錶初始為空,表中的節點不是新生成的,而是從原鍊錶當中一次 刪除 再逐個頭插到逆置表中。設逆置鍊錶的初始態為空表,刪除 已知鍊錶中 的第乙個節點,然後將它 插入 到逆置鍊錶的 表頭 即使得他成為逆置鍊錶中 新 的第乙個節...

就地鍊錶反轉 常見程式設計模式之就地反轉鍊錶

在很多問題中,我們需要對乙個鍊錶中的節點連線進行反轉,且通常需要原地進行,即不能使用額外的儲存空間。這時我們可以使用就地反轉鍊錶模式,該模式本質上是一種迭代解法,流程如下圖所示。首先設定乙個變數current指向鍊錶頭部,以及另乙個變數previous指向當前處理節點的前乙個節點。下一步我們需要將當...