翻轉鏈表合集

翻轉鏈表大概可以分爲如下四個,難度可以說是逐步增大的

  1. 翻轉整個鏈表(遞歸和非遞歸)
  2. 翻轉鏈表中的一部分
  3. 按k個一組進行翻轉(從前往後)
  4. 按k個一組進行翻轉(從後往前)

首先給出節點類定義

	class ListNode{
        int val;
        ListNode next;
        public ListNode(int val){
            this.val = val;
        }
    }

1. 翻轉整個鏈表

  • 遞歸翻轉整個鏈表,需要注意的是要記得head.next置爲空,避免形成環路。
	public ListNode reverseListRecursive(ListNode head) {
        if(head == null || head.next == null)    return head;
        ListNode reversed = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return reversed;
    }
  • 迭代翻轉鏈表,因爲翻轉後原本的頭結點會變成尾結點,因此開始循環前需要將頭結點的next置爲空。
	public ListNode reverseList(ListNode head) {
        if(head == null)    return head;
        ListNode prev = head, cur = head.next;
        prev.next = null;
        while(cur != null){
            ListNode temp = cur.next;
            cur.next = prev;
            prev = cur;
            cur = temp;
        }
        return prev;
    }

結果如下:
在這裏插入圖片描述

2. 翻轉部分鏈表

  翻轉序號fromIndex到toIndex之間的所有結點,假設鏈表頭結點序號爲1,需要注意的是fromIndex和toIndex可能會超出鏈表長度,此時的話不進行任何操作,直接返回。對於鏈表問題,聲明dummy頭部結點有時會帶來好處,這裏的話可以避免當fromIndex爲1時需要進行額外的判斷。
  首先找到需要翻轉的部分,然後進行翻轉(迭代),需要注意的是翻轉的時候,需要保存翻轉鏈表的前綴結點fromPre和後綴結點toNext,以便於後面重新拼接。

	public ListNode reverseList(ListNode head, int fromIndex, int toIndex){
        if(fromIndex <= 0 || toIndex <= 0)  return head;
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode fromPre = null, from = null, to = null, toNext = null;
        ListNode cur = dummy;
        int count = 0;
        while(cur != null){
            if(count == fromIndex-1)    fromPre = cur;
            if(count == fromIndex)    from = cur;
            if(count == toIndex){
                to = cur;
                toNext = cur.next;
                break;
            }
            cur = cur.next;
            count++;
        }
        if(from == null || to == null)  return head;    //fromIndex或者toIndex超出鏈表長度時
        fromPre.next = null;
        to.next = null;

        //翻轉from到to
        ListNode pre = from;
        cur = pre.next;
        pre.next = null;
        while(cur != null){
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;

        }
        fromPre.next = to;
        from.next = toNext;
        return dummy.next;
    }

翻轉1號到4號的所有節點,最終結果如下所示
在這裏插入圖片描述

3. 從前往後按k個一組進行翻轉

  相比於前一個問題,這次是要循環的翻轉,同樣的在每次翻轉前需要保存翻轉部分的前綴結點和後綴結點。

	public ListNode reverseListk(ListNode head, int k){      //每k個一組進行翻轉(從前往後數)
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode cur = dummy, preStartk = null, startk = null, endk = null;
        int index = 0;
        boolean reverse = false;	//是否需要翻轉
        while(cur != null){
            if(index == 0)  preStartk = cur;
            if(index == 1)  startk = cur;
            if(index == k){
                endk = cur;
                reverse = true;
            }
            cur = cur.next;
            index++;
            if(reverse){
                ListNode pre = startk, preNext = pre.next;
                while(preNext != cur){
                    ListNode temp = preNext.next;
                    preNext.next = pre;
                    pre = preNext;
                    preNext = temp;
                }
                preStartk.next = endk;
                startk.next = cur;
                //進行下一組翻轉
                cur = startk;
                index = 0;
                reverse = false;
            }
        }
        return dummy.next;
    }

按3個一組(從前往後)進行翻轉,結果如下
在這裏插入圖片描述

4. 從後往前按k個一組進行翻轉

  說實話,第三個問題從前往後翻轉k個已經是Leetcode中hard級別的題目了,這個可能更難一點。主要是要想到思路,可以分爲三個步驟,這兩個函數在上面的問題已經實現了。

  1. 翻轉整個鏈表
  2. 從前往後按k個每組進行翻轉
  3. 再一次翻轉整個鏈表
	public ListNode reversedListk(ListNode head, int k){      //逆序k個一組進行翻轉(從後往前數)
        head = reverseList(head);       //首先翻轉整個鏈表
        head = reverseListk(head, k);   //然後從前往後按k個每組進行翻轉
        head = reverseList(head);       //最後再翻轉整個鏈表
        return head;
    }

按3個一組(從後往前)進行翻轉,結果如下
在這裏插入圖片描述
其它的輔助代碼如下所示

public static void main(String[] args) {
        ListNode head = new ListNode(1), cur = head;
        for(int i = 2;i<8;i++){
            ListNode temp = new ListNode(i);
            cur.next = temp;
            cur = temp;
        }

        ReverseList solution = new ReverseList();
        System.out.println("翻轉前:"+solution.printListNode(head));

//        head = solution.reverseList(head);            //非遞歸翻轉
//        head = solution.reverseListRecursive(head);   //遞歸翻轉
//        head = solution.reverseList(head, 1, 4);      //只翻轉from到to的結點
//        head = solution.reverseListk(head, 3);        //從前往後k個一組進行翻轉
        head = solution.reversedListk(head, 3);     //從後往前k個一組進行翻轉

        System.out.println("翻轉後:"+solution.printListNode(head));
    }
    public String printListNode(ListNode head){
        StringBuffer s = new StringBuffer();
        ListNode cur = head;
        while(cur != null){
            s.append(cur.val).append(' ');
            cur = cur.next;
        }
        return s.toString();
    }

本文參考:我畫了20張圖,終於讓女朋友學會了翻轉鏈表

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章