1. 程式人生 > 實用技巧 >連結串列題彙總

連結串列題彙總

連結串列和演算法簡直不是一個東西,縱然做了很多演算法題,沒有專門訓練過連結串列是很容易成為炮灰的,用幾天時間把力扣上的連結串列題刷了記錄一下。

主要操作有快慢指標、連結串列合併、拆分連結串列、重組連結串列、連結串列移位、連結串列成環、連結串列相交、連結串列反轉、連結串列排序等,一般都是指標指來指去就夠了,儘量不要用陣列儲存,否則體現不出水平,時常需要判空。

以下題目大致按難度排序

力扣876:連結串列的中間節點

思路:1.常規的遍歷求長度,再跑到中點停下來 2.快慢指標,一個走一步,一個走兩步,快指標跑到末尾時慢指標就在中點

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 
*/ class Solution { public ListNode middleNode(ListNode head) { ListNode fast=head,slow=head; while(fast!=null && fast.next!=null){ slow=slow.next; fast=fast.next.next; } return slow; } }
力扣876

劍指offer18:刪除連結串列的節點

思路:遍歷過程中,下一個節點需要刪除的話,則把當前節點的指標指向下下個節點。特判頭節點。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        if(head==null)
            return null;
        ListNode cur=head;
        
//特判第一個節點 if(head.val==val) return head.next; while(cur!=null){ //下一個節點要刪除,就直接指向 下下個節點,退出 if(cur.next.val==val){ cur.next=cur.next.next; break; } cur=cur.next; } return head; } }
劍指offer18

劍指offer52:兩個連結串列的第一個公共節點

思路1:計算連結串列長度差k,長的先跑k步,短的再開始跑,會在交點相遇,若無交點,最後同時走到null。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA==headB)
            return headA;
        int len1=size(headA),len2=size(headB);
        //保證A是短的,B是長的
        if(len1>len2){
            ListNode temp=headA;
            headA=headB;
            headB=temp;
        }
        int k=Math.abs(len1-len2);
        ListNode cur1=headA,cur2=headB;
        //長的連結串列B先走k步
        for(int i=0;i<k;i++){
            cur2=cur2.next;
        }
        while(cur1!=cur2){
            cur1=cur1.next;
            cur2=cur2.next;
        }
        return cur1;
    }

    //獲取連結串列長度
    public static int size(ListNode head){
        int res=0;
        while(head!=null){
            res++;
            head=head.next;
        }
        return res;
    }
}
劍指offer52

思路2:一起跑,跑完換線跑,有交點會相遇,沒交點最後也都是跑完全程同時到null,不會死迴圈。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode cur1=headA,cur2=headB;
        while(cur1!=cur2){
            cur1=cur1==null?headB:cur1.next;
            cur2=cur2==null?headA:cur2.next;
        }
        return cur1;
    }
}
劍指offer52

力扣141:環形連結串列(騰訊面試)

思路:快慢指標,如果有環的話,不會遇到null;一快一慢跑進環裡總會相遇,一旦遇到null就可以退出。注意判空避免空指標異常。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head==null)
            return false;
        ListNode cur1=head,cur2=head.next;
        while(cur1!=null && cur2!=null){
            if(cur1==cur2){
                return true;
            }
            cur1=cur1.next;
            if(cur2.next!=null)
                cur2=cur2.next.next;
            else 
                break;
        }
        return false;
    }
}
力扣141

面試題02.05:連結串列求和

思路:逆序儲存,從首位開始往後走,注意三個點,進位、一條連結串列先為空、走完全程還有進位。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode cur1=l1,cur2=l2,ans=new ListNode(0),res=ans,last=ans;
        int x=0,sum=0;//進位
        while( cur1!=null || cur2!=null || x!=0){
            sum=x;
            if(cur1!=null && cur2!=null){
                sum=cur1.val+cur2.val+x;
                cur1=cur1.next;
                cur2=cur2.next;
            }else if(cur1!=null && cur2==null){
                sum=cur1.val+x;
                cur1=cur1.next;
            }else if(cur1==null && cur2!=null){
                sum=cur2.val+x;
                cur2=cur2.next;
            }
            x=sum/10;
            last=ans;
            ans.val=sum%10;
            ans.next=new ListNode(0);
            ans=ans.next;
        }
        last.next=null;
        return res;
    }
}
面試題02.05

力扣61:旋轉連結串列

思路:移動數位對長度求模,看移位把連結串列拆成兩條,記得斷開第一條連結串列末尾,尾再接上頭。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if(head==null)
            return null;
        int len=size(head);
        k=k%len;
        if(k==0 || head.next==null)
            return head;
        ListNode cur=head;
        for(int i=1;i<(len-k);i++){
            cur=cur.next;
        }
        ListNode temp=cur.next;//後面部分連結串列起點,不會是null
        cur.next=null;//前面部分末尾置空
        cur=temp;
        //找到末尾節點
        while(cur.next!=null){
            cur=cur.next;
        }
        //末尾節點->頭節點
        cur.next=head;
        return temp;
    }

    public static int size(ListNode head){
        int res=0;
        ListNode cur=head;
        while(cur!=null){
            res++;
            cur=cur.next;
        }
        return res;
    }

}
力扣61

力扣817:連結串列元件

思路:標記一下G中的數,遍歷一次連結串列,對連續存在於G的數區間計數,累計到答案中。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int numComponents(ListNode head, int[] G) {
        //最終答案  連續存在於G的數的數量
        int ans=0,num=0;
        //對G中的數做一下標記
        HashMap<Integer,Integer> map=new HashMap<>();
        for(int i=0;i<G.length;i++)
            map.put(G[i],1);
        ListNode cur=head;
        while(cur!=null){
            if( map.get(cur.val)!=null ){
                num++;
            }else {
                if(num!=0)
                    ans++;
                num=0;
            }
            cur=cur.next;
        }
        if(num!=0)
            ans++;
        return ans;
    }
}
力扣817

劍指offer24:反轉連結串列

思路:從頭走到尾,將當前結點指向上一個結點。

實現:上一個結點用last記錄,當前結點用cur記錄。下一個結點用t臨時儲存,因為修改了當前結點的指向就失去了下一個結點的位置,需要儲存。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null)
            return null;
        ListNode cur=head;
        ListNode t,last=null;
        while(cur != null){//當前不為空,把當前指向上一個
            t=cur.next;//臨時儲存下一個結點
            cur.next=last;//當前結點指向上一個結點
            last=cur;//更新下一輪的【上一個結點】為【當前結點】
            if(t==null)
                break;
            cur=t;//更新下一輪的【當前結點】為【下一個結點】
        }
        return cur;
    }
}
劍指offer24

力扣92:反轉連結串列II

思路:將連結串列分為3段,第1段正常,第2段反轉,第3段正常。注意第1段和第3段為空的情況。詳看程式碼,附圖解。

class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        if(head==null || head.next==null)
            return head;
        ListNode temp=new ListNode(-1),cur=head;
        //第1段為空 則 直接返回第2第3段連結串列
        if(m==1)
            return reverse(head,n-m+1);
        temp.next=head;
        int i=1;
        //止步於 第2段連結串列前
        while(i<(m-1)){
            cur=cur.next;
            i++;
        }
        cur.next=reverse(cur.next,n-m+1);
        return temp.next;
    }

    public static ListNode reverse(ListNode head,int x){
        ListNode tail=head,cur=head,last=null,temp;
        while(x-->0){
            temp=cur.next;//臨時儲存下一個節點
            cur.next=last;//當前節點指向上一個節點
            last=cur;//更新上一個節點,準備下一輪
            cur=temp;//移動當前節點
        }
        /*
        第3段連結串列不為空
               tail        last  cur
        null  ←  0  ←  0  ←  0    0  →  0  →  0

        第3段連結串列為空
               tail        last  cur
        null  ←  0  ←  0  ←  0   null
        */
        while(cur!=null){
            tail.next=cur;
            tail=tail.next;
            cur=cur.next;
        }
        return last;
    }
}
力扣92

劍指offer35:複雜連結串列的複製

題意:連結串列多出一個隨即指標隨機指向,要求複製出一模一樣的連結串列。

思路:主流解法都是在原連結串列中插入複製連結串列,最後拆分連結串列。需要保持原連結串列的記憶體地址不變,而不是單單分離出複製連結串列。

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {
        if(head==null)
            return null;
        //1.原連結串列的每個節點後插入複製節點
        Node cur=head;//
        while(cur!=null){
            //複製出當前節點
            Node temp=new Node(cur.val);
            //複製點插入cur和cur.next之間
            temp.next=cur.next;
            cur.next=temp;
            //移動當前節點
            cur=temp.next;
        }

        //2.處理random指標
        cur=head;
        while(cur!=null){
            if( cur.random!=null )
                cur.next.random=cur.random.next;//複製點->random的複製點
            cur=cur.next.next;
        }

        //3.分離連結串列
        cur=head.next;//第一個複製點
        Node res=head.next,pre=head;
        while(cur!=null){
            pre.next=cur.next;
            pre=cur.next;
            if(pre==null){//特判末尾節點
                cur.next=null;
                break;
            }
            cur.next=pre.next;
            cur=pre.next;
        }
        return res;
    }
}
劍指offer35

力扣143:重排連結串列(招銀面試)

題意:L0→L1→L2→L3→L4...→Ln-1→Ln 變成 L0→Ln→L1→Ln-1→L2→Ln-2...

思路:1.陣列儲存再雙指標從新組成 2.快慢指標找中點,拆分成兩條連結串列,第二條反轉,再合併;融合幾個簡單操作。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public void reorderList(ListNode head) {
        if(head==null || head.next==null)
            return;
        ListNode mid=mid(head);
        ListNode cur=head;
        //斷開連結串列
        while(cur!=null){
            if(cur.next==mid)
                cur.next=null;
            cur=cur.next;
        }
        mid=reverse(mid);//反轉後半部分連結串列
        merge(head,mid);
    }

    //獲取連結串列中點
    public static ListNode mid(ListNode head){
        //快慢指標
        ListNode fast=head,slow=head;
        while(fast!=null && fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        return slow;
    }

    //反轉連結串列
    public static ListNode reverse(ListNode head){
        if(head==null)
            return null;
        ListNode cur=head,t,last=null;
        while(cur != null){//當前不為空,把當前指向上一個
            t=cur.next;//臨時儲存下一個結點
            cur.next=last;//當前結點指向上一個結點
            last=cur;//更新下一輪的【上一個結點】為【當前結點】
            if(t==null)
                break;
            cur=t;//更新下一輪的【當前結點】為【下一個結點】
        }
        return cur;
    }

    //交替合併連結串列
    public static ListNode merge(ListNode l1,ListNode l2){
        ListNode cur1=l1,cur2=l2,temp1,temp2;
        while(cur1!=null && cur2!=null){
            temp1=cur1.next;
            cur1.next=cur2;
            temp2=cur2.next;
            //l1.size()<=l2.size(),防止丟失l2最後一個
            if(temp1==null)
                break;
            cur2.next=temp1;
            cur1=temp1;
            cur2=temp2;
        }
        cur1.next=cur2;
        return l1;
    }
}
力扣143

力扣86:分隔連結串列

思路:樣例看了很久才看明白什麼意思,小於x的放前面,大於等於x的放後面,在初始連結串列中的相對位置不變。小於x的節點按順序放在一條連結串列裡,大於等於x的節點按順序放在另一條連結串列裡,最後把兩條連結串列接起來。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode partition(ListNode head, int x) {
        ListNode l1=new ListNode(-1),l2=new ListNode(-1),cur=head;
        ListNode res1=l1,res2=l2;
        while(cur!=null){
            if(cur.val<x){
                l1.next=cur;
                l1=l1.next;
            }else{
                l2.next=cur;
                l2=l2.next;
            }
            cur=cur.next;
        }
        l2.next=null;
        l1.next=res2.next;
        return res1.next;
    }
}
力扣86

力扣725:分隔連結串列

思路:均分長度取整,多出的節點分給前面的連結串列,人均一個。截斷連結串列時注意記錄下一條連結串列的起點,節點若不夠分則後面的連結串列為空,需要判空。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode[] splitListToParts(ListNode root, int k) {

        ListNode[] ans=new ListNode[k];
        if(root==null)
            return ans;
        int len=0;
        ListNode cur=root;
        while(cur!=null){
            len++;
            cur=cur.next;
        }
        //均分之後的餘數,註定前r個連結串列多1個節點,x是均分節點數
        int r=len%k,x=len/k;
        //第i個連結串列 當前連結串列長度
        int i=0,num=0;
        cur=root;
        ListNode start,temp=root;
        //獲取完最後一個連結串列沒有對cur置空,需要用i<k退出
        while( cur!=null && i<k){
            //r個較長的連結串列不會必定遇到空的情況
            if(i<r){
                start=temp;
                cur=start;
                for(int j=1;j<(1+x);j++){
                    cur=cur.next;
                }
                temp=cur.next;//儲存下一個起點
                cur.next=null;//截斷連結串列尾
                ans[i]=start;//儲存每段連結串列頭
            }else{
                start=temp;
                cur=start;
                for(int j=1;j<x && cur!=null;j++)
                    cur=cur.next;
                //若遇到空說明連結串列已經被拆完了,直接退出
                if(cur==null){
                    ans[i]=start;
                    break;
                }
                temp=cur.next;
                cur.next=null;
                ans[i]=start;
            }
            i++;
        }
        return ans;
    }
}
力扣725

力扣148:排序連結串列

思路:歸併排序,找中點分割+合併。空間複雜度不符合題目要求。但就是不想看迭代,遇到了算我倒黴。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        return segment(head);
    }

    //歸併排序,分割一條連結串列為兩個連結串列再合併成一條連結串列
    public static ListNode segment(ListNode head){
        if(head==null)
            return null;
        if(head.next==null)
            return head;
        //快慢指標找中點
        ListNode slow=head,fast=head.next;
        while(fast!=null && fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        ListNode l2=segment(slow.next);
        slow.next=null;//可能並非均分,將就著斷
        ListNode l1=segment(head);
        return merge(l1,l2);
    }

    //合併有序連結串列
    public static ListNode merge(ListNode l1,ListNode l2){
        ListNode res=new ListNode(0);
        ListNode cur1=l1,cur2=l2,now=res;
        while(cur1!=null && cur2!=null){
            if(cur1.val<cur2.val){
                now.next=cur1;
                cur1=cur1.next;
            }else{
                now.next=cur2;
                cur2=cur2.next;
            }
            now=now.next;
        }
        //誰沒跑完就把它接到尾巴
        now.next=cur2==null?cur1:cur2;
        return res.next;
    }
}
力扣148

力扣234:迴文連結串列

思路:找中點分割,第二條反轉,一起從頭遍歷看值是否相同。注意空連結串列返回true。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head==null || head.next==null)
            return true;
        ListNode mid=mid(head);
        ListNode cur=head;
        //斷開連結串列
        while(cur!=null){
            if(cur.next==mid)
                cur.next=null;
            cur=cur.next;
        }
        mid=reverse(mid);
        ListNode cur1=head,cur2=mid;
        //cur1.size()<=cur2.size()
        while(cur1!=null){
            if(cur1.val!=cur2.val)
                return false;
            cur1=cur1.next;
            cur2=cur2.next;
        }
        return true;
    }

    //獲取連結串列中點
    public static ListNode mid(ListNode head){
        //快慢指標
        ListNode fast=head,slow=head;
        while(fast!=null && fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        return slow;
    }

    //反轉連結串列
    public static ListNode reverse(ListNode head){
        if(head==null)
            return null;
        ListNode cur=head,t,last=null;
        while(cur != null){//當前不為空,把當前指向上一個
            t=cur.next;//臨時儲存下一個結點
            cur.next=last;//當前結點指向上一個結點
            last=cur;//更新下一輪的【上一個結點】為【當前結點】
            if(t==null)
                break;
            cur=t;//更新下一輪的【當前結點】為【下一個結點】
        }
        return cur;
    }

}
力扣234

力扣382:連結串列隨機節點

思路:蓄水池抽樣演算法

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {

    /** @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node. */
    static ListNode head;
    public Solution(ListNode head) {
        this.head=head;
    }
    
    /** Returns a random node's value. */
    public int getRandom() {
        int cnt=0,ans=0;
        Random r=new Random();
        ListNode cur=head;
        while(cur!=null){
            cnt++;
            if(r.nextInt()%cnt==0)
                ans=cur.val;
            cur=cur.next;
        }
        return ans;

    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(head);
 * int param_1 = obj.getRandom();
 */
力扣382

力扣328:奇偶連結串列(映客筆試)

思路:分別用兩條連結串列儲存奇偶節點,最後拼接,注意偶連結串列末尾置空,記錄偶連結串列頭。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode oddEvenList(ListNode head) {
        if(head==null || head.next==null)
            return head;
        ListNode cur1=head,cur2=head.next,cur=head.next.next;
        ListNode temp=head.next;//防止丟失偶數連結串列的頭部
        int cnt=1;
        while(cur!=null){
            if(cnt%2==1){//奇數
                cur1.next=cur;
                cur1=cur1.next;
            }else{
                cur2.next=cur;
                cur2=cur2.next;
            }
            cur=cur.next;
            cnt++;
        }
        cur1.next=temp;
        cur2.next=null;
        cur=head;
        return head;
    }
}
力扣328

力扣1290:二進位制連結串列轉整數

思路:反轉,遍歷,累計val*2的冪次方。

class Solution {
    static int[] two=new int[30];
    public int getDecimalValue(ListNode head) {
        if(head==null)
            return 0;
        two[0]=1;
        for(int i=1;i<30;i++)
            two[i]=two[i-1]*2;
        head=reverse(head);
        ListNode cur=head;
        int i=0,ans=0;
        while(cur!=null){
            ans+=two[i++]*cur.val;
            cur=cur.next;
        }
        return ans;
    }

    //反轉連結串列
    public static ListNode reverse(ListNode head){
        if(head==null)
            return null;
        ListNode cur=head,t,last=null;
        while(cur != null){//當前不為空,把當前指向上一個
            t=cur.next;//臨時儲存下一個結點
            cur.next=last;//當前結點指向上一個結點
            last=cur;//更新下一輪的【上一個結點】為【當前結點】
            if(t==null)
                break;
            cur=t;//更新下一輪的【當前結點】為【下一個結點】
        }
        return cur;
    }
}
力扣1290

面試題04.03:特定深度節點連結串列

思路:bfs過程中,每次把一層的節點弄成連結串列,先放在ArrayList裡,最後再用toArray轉陣列。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode[] listOfDepth(TreeNode tree) {
        if(tree==null)  
            return null;
        LinkedList<TreeNode> list=new LinkedList<>();
        ArrayList<ListNode> res = new ArrayList<>();
        list.add(tree);
        ListNode temp=new ListNode(-1),cur;//偽節點頭
        while(list.size()>0){
            cur=temp;
            //獲取當前層的節點數,拼接一條連結串列
            int len=list.size();
            for(int i=0;i<len;i++){
                TreeNode now=list.poll();
                cur.next=new ListNode(now.val);
                cur=cur.next;
                //略過空的
                if(now.left!=null)
                    list.add(now.left);
                if(now.right!=null)
                    list.add(now.right);
            }
            res.add(temp.next);
        }
        return res.toArray(new ListNode[]{});//ArrayList轉陣列
    }
}
面試題04.03

力扣23:合併K個升序連結串列(映客筆試)

思路1:優先佇列

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        //優先佇列
        Comparator<ListNode> comparator=new Comparator<ListNode>() {
            public int compare(ListNode o1, ListNode o2) {
                return o1.val<o2.val?-1:1;
            }
        };
        Queue<ListNode> que=new PriorityQueue<ListNode>(comparator);
        ListNode temp,cur;
        for(int i=0;i<lists.length;i++){
            cur=lists[i];
            while(cur!=null){
                que.add(cur);
                temp=cur;
                cur=cur.next;
                temp.next=null;//每個節點的next都置空,防止成環
            }
        }
        ListNode head=new ListNode(-1);
        cur=head;
        while(que.size()>0){
            cur.next=que.poll();
            cur=cur.next;
        }
        return head.next;
    }
}
優先佇列

思路2:歸併排序

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists.length==0)
            return null;
        return sort(0,lists.length-1,lists);
    }

    public static ListNode sort(int l,int r,ListNode[] lists){
        if(l==r)
            return lists[l];
        int mid=(l+r)/2;
        ListNode l1=sort(l,mid,lists);
        ListNode l2=sort(mid+1,r,lists);
        return merge(l1,l2);
    }

    //合併有序連結串列
    public static ListNode merge(ListNode l1,ListNode l2){
        ListNode res=new ListNode(0);
        ListNode cur1=l1,cur2=l2,now=res;
        while(cur1!=null && cur2!=null){
            if(cur1.val<cur2.val){
                now.next=cur1;
                cur1=cur1.next;
            }else{
                now.next=cur2;
                cur2=cur2.next;
            }
            now=now.next;
        }
        //誰沒跑完就把它接到尾巴
        now.next=cur2==null?cur1:cur2;
        return res.next;
    }
}
歸併排序

力扣147:對連結串列進行插入排序

思路:遍歷過程中,維持前面部分有序,嚴格遞增。當前節點需要調整位置時,從頭遍歷找到適當的插入位置(前面小於當前節點,後面大於當前節點)。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode insertionSortList(ListNode head) {
        if(head==null || head.next==null)
            return head;
        //偽頭節點
        ListNode dummy=new ListNode(0);
        dummy.next=head;
        //pre為cur的前一個節點,通過cur=pre.next去移動即可
        ListNode pre=head,cur=head.next,temp; 
        while(cur!=null){
            if(pre.val<=cur.val){
                pre=pre.next;
            }else{
                temp=dummy;
                //找到最後一個位置比cur小的位置,保證遞增嚴格
                while(temp.next.val<=cur.val){
                    temp=temp.next;
                }
                /*
                          temp   pre   cur
                1  →  2  →  4  →  8  →  5  →  6...
                */
                pre.next=cur.next;
                cur.next=temp.next;
                temp.next=cur;
                /*
                          temp   cur   pre
                1  →  2  →  4  →  5  →  8  →  6...
                */
            }
            cur=pre.next;
        }
        return dummy.next;
    }
}
力扣147

力扣25:K個一組翻轉連結串列

思路:對每一小段需要翻轉k個節點的子連結串列,需要子連結串列的頭節點,子連結串列的前驅節點,最後返回子連結串列的尾節點當作新的前驅節點。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummy=new ListNode(-1),cur=head;
        dummy.next=head;
        int len=0;
        while(cur!=null){
            cur=cur.next;
            len++;
        }
        cur=dummy;
        int t=len/k;//反轉t段長度為k的連結串列
        while(t-->0){
            cur=reverseK(cur,cur.next,k);
        }
        cur=dummy.next;
        return dummy.next;
    }

    //對後續k個節點進行反轉
    public static ListNode reverseK(ListNode pre,ListNode head,int k){
        if(head==null)
            return head;
        //反轉時需要指向上一個節點,當前節點,臨時儲存下一個節點
        ListNode last=null,cur=head,temp;
        while(k-->0 && cur!=null){
            //整條鏈的前驅節點不斷往後指,改變連結串列指向
            pre.next=cur;
            temp=cur.next;
            cur.next=last;
            last=pre.next;
            cur=temp;
        }
        //返回反轉後的連結串列尾,當作下k個節點連結串列的前驅節點
        head.next=cur;
        return head;
    }
}
力扣25

寫在最後,Java用容器很容易解出這些題,例如用ArrayList存起來後,翻轉、找中點等操作用下標即可。筆試的時候可以騙分,實在不行寫一下極端情況(head==null || head.next==null)返回head大概率也能騙點分,悔不當初;面試手撕用容器就落了下乘。做題需要留意的有這些變數,頭節點head、偽頭節點(頭節點的前驅)dummy、遍歷到的當前節點cur、當前節點的上一個節點last、臨時儲存下一個節點temp(翻轉後無法通過cur=cur.next獲取,先用temp存一下),以及各種情況下判空break,儘量有屬於自己的命名風格以及修改操作習慣,看了題解也嘗試用自己的風格寫出來,在草稿紙上塗塗畫畫,以後遇到就不成問題了。別無他長,惟手熟爾。