Link連結串列的反轉
阿新 • • 發佈:2018-12-12
尊重原創,轉載請標明出處 http://blog.csdn.net/abcdef314159
上一篇簡單介紹了LinkedList,程式碼都很簡單,基本上沒有什麼可說的,這一篇就來介紹一下連結串列的使用,反轉連結串列,這個連結串列和LinkedList是不一樣的,因為LinkedList是雙向連結串列,不需要反轉也可以從後往前遍歷,這裡建立的連結串列是單向的,只能從前往後,不能從後往前,如果想要從後往前遍歷,就需要把連結串列反轉,先建立一個節點類
上面是一個節點類,下面再寫一個列印的類public class LinkNode { public int val; public LinkNode next; public LinkNode(int x) { val = x; } }
public static void printLink(LinkNode mLink) {
LinkNode tempNoe = mLink;
while (tempNoe != null) {
System.out.print(tempNoe.val + "\t");
tempNoe = tempNoe.next;
}
System.out.println();
}
先來看第一個反轉的方法
下面初始化一下連結串列public static LinkNode reverseList1(LinkNode head) { //反轉當前連結串列,prev是指前一個節點,表示已經反轉好的連結串列,最開始的時候為null LinkNode prev = null; while (head != null) { //這裡可以認為head是當前節點,因為下面通過不停的賦值,讓head始終指向當前節點, //tem是臨時節點,儲存的是當前節點的下一個,如果不儲存,在執行head.next = prev //的時候把當前節點之後的節點都搞丟了。 LinkNode tmp = head.next; //讓前一個節點等於當前節點的下一個(前一個節點是已經反轉好的),這個是反轉的關鍵, //因為反轉是從頭結點開始的,而prev可以認為是前面幾個已經反轉好的節點,然後掛載在 //當前節點的下一個,所以這裡不停的通過white迴圈,然後不斷的把已反轉好的prev掛載到 //當前節點後面,最終實現了連結串列的反轉。 head.next = prev; //掛載完之後讓head等於prev節點,表示已經反轉過來了,prev表示反轉之後的連結串列 prev = head; //讓儲存的tmp節點等於head,實際就相當於在往後挪了一位。然後在迴圈 head = tmp; } return prev;//返回反轉後的節點。 }
在呼叫reverseList1方列印一下,看一下結果public static LinkNode initLink() { LinkNode node1 = new LinkNode(1); LinkNode node2 = new LinkNode(2); LinkNode node3 = new LinkNode(3); LinkNode node4 = new LinkNode(4); LinkNode node5 = new LinkNode(5); LinkNode node6 = new LinkNode(6); node1.next = node2; node2.next = node3; node3.next = node4; node4.next = node5; node5.next = node6; return node1; }
1 2 3 4 5 6
6 5 4 3 2 1
上一排是正常列印,下一排是經過反轉之後的結果,實現了連結串列的反轉,那麼還有沒有其他方式,繼續看
public static LinkNode reverseList2(LinkNode head) {
//可以把head節點當做當前節點,如果當前節點為null,或者沒有後繼節點,直接返回當前節點,
//因為就一個節點沒法反轉
if (head == null || head.next == null)
return head;
//遞迴遍歷,從當前節點的下一個節點開始,prev表示已經反轉好的,所以這一步執行完之後
//當前節點head之後的都已反轉完成(當前節點head沒有反轉)
LinkNode prev = reverseList2(head.next);
//既然head.next已經反轉完成,那麼head.next顯然是已經反轉到連結串列最後了,然後再讓head等於head.next的下一個,
//把head節點放到已經反轉的連結串列的最後。其實每次head.next都是為null,因為在下一步把它置為null了,然後
//再讓head等於head.next的下一個
head.next.next = head;
//然後讓head.next節點為空,因為這裡當前節點head是已經反轉好的最後一個節點,所以可以置為null。
head.next = null;
return prev;
}
再看一下列印結果
1 2 3 4 5 6
6 5 4 3 2 1
也實現了反轉,如果不好理解可以在改一下,接著往下看
public static LinkNode reverseList3(LinkNode head) {
if (head == null || head.next == null)
return head;
//在reverseList2中可能這一點不太好理解,雖然prev是已經反轉之後的列表,但是prev指向的
//是表頭,那怎麼樣才能把head新增到表尾,只能通過迴圈,找到最後一個。
LinkNode prev = reverseList3(head.next);
LinkNode current = prev;
while (current.next != null)
current = current.next;//找最後一個
current.next = head;//把head新增到最後一個的next中
head.next = null;
return prev;
}
接著往下看
public static LinkNode reverseList4(LinkNode head) {
//這個和reverseList1差不多
if (null == head) {
return head;
}
//還是一樣,pre是表示已經反轉好的連結串列,這裡的pre暫且認為他是等於head節點的,僅僅等於head這一個節點
//不包含head後面的節點,暫且這樣認為,雖然不對
LinkNode pre = head;
LinkNode cur = head.next;//cur認為是當前節點
LinkNode _next;//_next認為是當前節點的下一個節點
while (null != cur) {
_next = cur.next;//儲存當前節點的下一個節點
//pre是表達已經反轉好的連結串列,讓他等於當前連結串列的下一個,就是掛載在當前連結串列的下一個
cur.next = pre;
//掛載完之後表示又反轉好了一個,然後讓他等於pre,因為pre就是已經反轉好的連結串列
pre = cur;
//跳到下一個連結串列迴圈,繼續迴圈
cur = _next;
}
//將原連結串列的頭節點的下一個節點置為null,再將反轉後的頭節點賦給head,這裡為什麼要置null,因為如果不置null,
//head.next一直是有值的,這個會造成死迴圈,那為什麼reverseList1中最後沒有把head.next置為null,那是因為一開始
//的時候就讓head.next = prev; 而最開始prev是大於null的,其實已經把prev置為null了
head.next = null;
head = pre;//反轉之後的連結串列賦值給head
return head;//其實上一步也可以不賦值,直接返回pre就可。
}
繼續看
public static LinkNode reverseList5(LinkNode prev, LinkNode curr) {
//prev是前一個節點,表示已經反轉好的連結串列,可以為null,如果curr是first節點,那麼prev就是null,
//就表示還沒有排序
LinkNode next = curr.next;
if (next == null) {
// 沒有下個節點則停止迴圈。
curr.next = prev;
return curr;
}
// 將反轉好的節點prev賦值給當前節點的下一個,這裡在第一次呼叫的時候prev為null,curr是頭結點,
//所以第一步的時候就已經把尾節點置為null了。
curr.next = prev;
//這裡是通過遞迴,不斷的取出當前節點,然後在不斷的讓已反轉好的節點掛載到當前節點中
return reverseList5(curr, next);
}
繼續
public static LinkNode reverseList6(LinkNode head) {
//cuur可以認為是當前節點
LinkNode curr = head.next;
//_next表示是當前節點curr節點的下一個節點,在下面迴圈中用到
LinkNode _next = curr.next;
//讓頭結點孤立起來,讓他沒有後繼節點,成為孤零零的一個節點,這時我們可以認為head節點是已經反轉好的節點
head.next = null;
while (_next != null) {
//讓已經反轉好的節點head成為當前節點的下一個,實現反轉
curr.next = head;
//因為curr連結串列已經是反轉之後的了,所以讓curr等於head,head表示已經反轉好的連結串列
head = curr;
//讓下一個節點成為當前節點,然後下面迴圈
curr = _next;
//移動到下一個節點,繼續
_next = _next.next;
}
//curr是最後一個節點,因為上面迴圈結束的條件是_next != null,而_next可以認為是curr的下一個節點
//當curr的下一個節點為null的時候,說明curr是尾節點,所以這裡把前面已經反轉好的節點掛載到最後一個節點,
//最終實現所以節點反轉。
curr.next = head;
return curr;
}
繼續
public static LinkNode reverseList7(LinkNode head, LinkNode next) {
//這個和reverseList5很相似,如果下一個節點為null,直接返回
if (next == null) {
return head;
}
//head可以認為是已經反轉好的,如果head節點的下一個是next,要把head節點的下一個置為null,
//否則會出現死迴圈,因為head暫且認為是已經反轉好的,然後會掛載到next的下面,如果不置為null,
//那麼相互掛載,就會出現死迴圈。這裡要明白這一點,head的next是已經反轉好的head節點的下一個,
// 不是已經反轉好的最後一個
if (head.next.equals(next)) {
head.next = null;
}
if (next.next != null) {
//儲存next的下一個,下面遞迴的時候傳進去
LinkNode next2 = next.next;
//head可以認為是反轉好的,直接賦值給next的下一個
next.next = head;
//遞迴
return reverseList7(next, next2);
}
//將反轉好的head賦值給next的下一個
next.next = head;
return next;
}
接著往下看
public static LinkNode reverseList8(LinkNode head) {
LinkNode tail = head.next;
if (tail.next != null) {
LinkNode prev = reverseList8(tail);// prev表示已經反轉好的
LinkNode last = prev;
while (last.next != null)
last = last.next;//找到最後一個
last.next = head;//掛載到最後一個上
head.next = null;
return prev;
}
tail.next = head;
head.next = null;
return tail;
}
再看
public static LinkNode reverseList9(LinkNode head) {
//這個和第一個很相似
if (head == null || head.next == null) {
return head;
}
LinkNode preNode = null;
LinkNode curNode = head;
LinkNode nextNode;
while (curNode != null) {
nextNode = curNode.next;
curNode.next = preNode;
preNode = curNode;
curNode = nextNode;
}
return preNode;
}