• 链表题汇总


    链表和算法简直不是一个东西,纵然做了很多算法题,没有专门训练过链表是很容易成为炮灰的,用几天时间把力扣上的链表题刷了记录一下。

    主要操作有快慢指针、链表合并、拆分链表、重组链表、链表移位、链表成环、链表相交、链表反转、链表排序等,一般都是指针指来指去就够了,尽量不要用数组存储,否则体现不出水平,时常需要判空。

    以下题目大致按难度排序

    力扣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段为空的情况。详看代码,附图解。

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) { val = x; }
     * }
     */
    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
            */
            tail.next=cur;
            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,尽量有属于自己的命名风格以及修改操作习惯,看了题解也尝试用自己的风格写出来,在草稿纸上涂涂画画,以后遇到就不成问题了。别无他长,惟手熟尔。

  • 相关阅读:
    RADAR毫米波雷达传感器
    固态LiDAR,半固态混合LiDAR,机械LiDAR
    Lidar激光雷达市场
    echarts 环形图中自定义文字
    uni-app base64 无法显示问题
    实战二(上):针对非业务的通用框架开发,如何做需求分析和设计?
    实战一(下):如何实现一个遵从设计原则的积分兑换系统?
    实战一(上):针对业务系统的开发,如何做需求分析和设计?
    学而不记,不学无异 -- English learning
    springmvc 传入返回参数更改
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/13848032.html
Copyright © 2020-2023  润新知