1.
https://www.cnblogs.com/kubixuesheng/p/4394509.html
定義一個方法(函數),實現輸入一個鏈表的頭結點,然后可以反轉這個鏈表的方向,並輸出反轉之后的鏈表的頭結點。
typedef struct Node{
int data;
Node *next;
} Node, *List;
鏈表類的問題,涉及到了很多指針的操作,需要嚴謹的分析,全面的分析問題之后,在開始寫代碼,磨刀不誤砍柴工!反轉鏈表,直接的想法,就是把鏈表中指針的方向反轉就可以了,如圖所示:

假設 i 結點之前,我們把所有的結點的指針都已經反轉了,那么自然 i 和以后的結點鏈接發生了斷裂!如下圖;

這樣的話,無法繼續遍歷 i 以后的結點了,那么自然想到,在斷鏈之前,提前保存之前的狀態。那么自然想到定義三個指針,分別指向當前結點 i,i 的后繼 j,i 的前驅 h 結點。保存斷鏈之前的三個結點的連接狀態。然后,假設沒問題了,那么繼續反轉完畢,最后鏈表的尾結點就是反正鏈表的頭結點了,也就是 next 為 null 的結點,是原始鏈表的尾結點。
#include <iostream>
using namespace std;
typedef struct Node{
int data;
Node *next;
} Node, *List;
Node * reverseList(List head){
//定義三個指針,保存原來的連接的狀態
//當前結點指針
Node *pnow = head;
//當前結點的前驅指針,初始化是 NULL
Node *pre = NULL;
//當前結點的后繼指針,初始化也是 null
Node *pnext = NULL;
//定義尾指針
Node *tail = NULL;
//開始遍歷鏈表
while(pnow != NULL){
//如果當前結點不是 null,那么初始化 pnext 指針指向當前結點的下一個結點
pnext = pnow->next;
//如果找到了尾結點,初始化 tail 指針
if(NULL == pnext){
tail = pnow;
}
//進行鏈表的反轉,當前結點的 next 指針指向前一個結點,實現鏈表方向的反轉,此時發生了斷鏈
pnow->next = pre;
//勿忘斷鏈的情形,需要使用 pre 指針保存狀態,pre 等價於是后移一個結點
pre = pnow;
//pnow 后移一個結點
pnow = pnext;
}
return tail;
}
定義的這個三個指針,目的就是防止斷鏈之后無法繼續遍歷鏈表以后的結點,實現全部的反轉。當 pnow 的 next 指向 pnow 的前驅pre(初始化是 null)的時候,已經實現了 pnow 和前驅pre的方向反轉,但是 pnow 此時就和后繼pnext斷鏈了,那么使用 pre 后移的方式,指向 pnow,同時 pnow 也后移,指向 pnext,而 pnext 繼續指向更新之后的 pnow 的 next 結點即可。從而實現了狀態的保存,繼續遍歷全部結點,實現鏈表反轉。
注意關於鏈表問題的常見注意點的思考:
1、如果輸入的頭結點是 NULL,或者整個鏈表只有一個結點的時候
2、鏈表斷裂的考慮
下面看看遞歸的實現方式
遞歸的方法其實是非常巧的,它利用遞歸走到鏈表的末端,然后再更新每一個node的next 值 ,實現鏈表的反轉。而newhead 的值沒有發生改變,為該鏈表的最后一個結點,所以,反轉后,我們可以得到新鏈表的head。
//遞歸方式
Node * reverseList(List head)
{
//如果鏈表為空或者鏈表中只有一個元素
if(head == NULL || head->next == NULL)
{
return head;
}
else
{
//先反轉后面的鏈表,走到鏈表的末端結點
Node *newhead = reverseList(head->next);
//再將當前節點設置為后面節點的后續節點
head->next->next = head;
head->next = NULL;
return newhead;
}
}
========================
2.
https://blog.csdn.net/u013132035/article/details/80589657
題目:
定義一個函數,輸入一個鏈表的頭結點,反轉該鏈表並輸出反轉后鏈表的頭結點。鏈表結點定義如下:
struct ListNode{
int m_nKey,
ListNode * m_pNext;
}
思路:
為了正確地反轉一個鏈表,需要調整鏈表中指針的方向。為了將復雜的過程說清楚,這里借助於下面的這張圖片。
上面的圖中所示的鏈表中,h、i和j是3個相鄰的結點。假設經過若干操作,我們已經把h結點之前的指針調整完畢,這個結點的m_pNext都指向前面的一個結點。接下來我們把i的m_pNext指向h,此時結構如上圖所示。
從上圖注意到,由於結點i的m_pNext都指向了它的前一個結點,導致我們無法在鏈表中遍歷到結點j。為了避免鏈表在i處斷裂,我們需要在調整結點i的m_pNext之前,把結點j保存下來。
即在調整結點i的m_pNext指針時,除了需要知道結點i本身之外,還需要i的前一個結點h,因為我們需要把結點i的m_pNext指向結點h。同時,還需要實現保存i的一個結點j,以防止鏈表斷開。故我們需要定義3個指針,分別指向當前遍歷到的結點、它的前一個結點及后一個結點。故反轉結束后,新鏈表的頭的結點就是原來鏈表的尾部結點。尾部結點為m_pNext為null的結點。
代碼實現:
public class ListNode {
public int data;
public ListNode next;
}
public ListNode reverseList(ListNode pHead){
ListNode pReversedHead = null; //反轉過后的單鏈表存儲頭結點
ListNode pNode = pHead; //定義pNode指向pHead;
ListNode pPrev = null; //定義存儲前一個結點
while(pNode != null){
ListNode pNext = pNode.next; //定義pNext指向pNode的下一個結點
if(pNext==null){ //如果pNode的下一個結點為空,則pNode即為結果
pReversedHead = pNode;
}
pNode.next = pPrev; //修改pNode的指針域指向pPrev
pPrev = pNode; //將pNode結點復制給pPrev
pNode = pNext; //將pNode的下一個結點復制給pNode
}
return pReversedHead;
}
小結:
這道題考查我們是否真正的理解了單鏈表的結構,以及在反轉單鏈表時對指針的修改。
擴展:
我們知道這個題其實是可以用遞歸實現使單鏈表變成反轉鏈表。
代碼實現:
public ListNode reverseList3(ListNode pHead){
if(pHead==null || pHead.next == null){ //如果沒有結點或者只有一個結點直接返回pHead
return pHead;
}
ListNode pNext = pHead.next; //保存當前結點的下一結點
pHead.next = null; //打斷當前結點的指針域
ListNode reverseHead = reverseList3(pNext); //遞歸結束時reverseHead一定是新鏈表的頭結點
pNext.next = pHead; //修改指針域
return reverseHead;
}
================
3.
https://www.jianshu.com/p/36ed87e1937a
要求很簡單,輸入一個鏈表,反轉鏈表后,輸出新鏈表的表頭。
反轉鏈表是有2種方法(遞歸法,遍歷法)實現的,面試官最愛考察的算法無非是斐波那契數列和單鏈表反轉,遞歸方法實現鏈表反轉比較優雅,但是對於不了解遞歸的同學來說還是有理解難度的。
遞歸法
總體來說,遞歸法是從最后一個Node開始,在彈棧的過程中將指針順序置換的。
為了方便理解,我們以 1->2->3->4這個鏈表來做演示。輸出的效果是4->3->2->1
首先定義Node:
public static class Node { public int value; public Node next; public Node(int data) { this.value = data; } }
反轉方法如下:
public Node reverse(Node head) { if (head == null || head.next == null) return head; Node temp = head.next; Node newHead = reverse(head.next); temp.next = head; head.next = null; return newHead; }
遞歸實質上就是系統幫你壓棧的過程,系統在壓棧的時候會保留現場。
我們來看是怎樣的一個遞歸過程:1->2->3->4
- 程序到達Node newHead = reverse(head.next);時進入遞歸
- 我們假設此時遞歸到了3結點,此時head=3結點,temp=3結點.next(實際上是4結點)
- 執行Node newHead = reverse(head.next);傳入的head.next是4結點,返回的newHead是4結點。
- 接下來就是彈棧過程了
- 程序繼續執行 temp.next = head就相當於4->3
- head.next = null 即把 3結點指向4結點的指針斷掉。
- 返回新鏈表的頭結點newHead
注意:當retuen后,系統會恢復2結點壓棧時的現場,此時的head=2結點;temp=2結點.next(3結點),再進行上述的操作。最后完成整個鏈表的翻轉。
遍歷法
遍歷法就是在鏈表遍歷的過程中將指針順序置換
遍歷法實現圖
先上代碼:
public static Node reverseList(Node node) { Node pre = null; Node next = null; while (node != null) { next = node.next; node.next = pre; pre = node; node = next; } return pre; }
依舊是1->2->3->4
- 准備兩個空結點 pre用來保存先前結點、next用來做臨時變量
- 在頭結點node遍歷的時候此時為1結點
- next = 1結點.next(2結點)
- 1結點.next=pre(null)
- pre = 1結點
- node = 2結點
- 進行下一次循環node=2結點
- next = 2結點.next(3結點)
- 2結點.next=pre(1結點)=>即完成2->1
- pre = 2結點
- node = 3結點
- 進行循環...

