力扣刷题篇之链表2

系列文章目录


目录

系列文章目录

前言

一、链表高精度加法

二、链表的合并

 三、链表中的双指针技巧

总结


前言

本系列是个人力扣刷题,本文是链表。刷题顺序按照[力扣刷题攻略] Re:从零开始的力扣刷题生活 - 力扣(LeetCode)

力扣刷题篇之链表2_第1张图片


一、链表高精度加法

力扣刷题篇之链表2_第2张图片

力扣刷题篇之链表2_第3张图片

用carry记录进位。

/**
 * 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 addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode root=new ListNode(0);
        ListNode cursor=root;
        int carry=0;
        while(l1!=null||l2!=null||carry!=0){
            int l1Val=l1!=null? l1.val:0;
            int l2Val=l2!=null? l2.val:0;
            int sumVal=l1Val+l2Val+carry;
            carry=sumVal/10;
            ListNode sumNode=new ListNode(sumVal%10);
            cursor.next=sumNode;
            cursor=sumNode;

            if(l1!=null) l1=l1.next;
            if(l2!=null) l2=l2.next;
        }
        if(carry!=0)cursor.next=new ListNode(carry);
        return root.next;
    }
}

力扣刷题篇之链表2_第4张图片

力扣刷题篇之链表2_第5张图片

力扣刷题篇之链表2_第6张图片

这个跟上一题不一样的是,这个向前进位,上一个向后进位,那么我们先把两个反转,相加后再反转。反转链表的函数要敲熟,这里是写的以头进,整个链表反转,我记得之前有个题写了左边和右边界之间的部分反转。

/**
 * 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 addTwoNumbers(ListNode l1, ListNode l2) {
        if(l1==null)return l2;
        if(l2==null)return l1;

        ListNode head1=reverseNode(l1);
        ListNode head2=reverseNode(l2);
        ListNode res=new ListNode();
        ListNode temp=res;
        int val1=0,val2=0;
        int carry=0;
        while(head1!=null||head2!=null){
            val1=head1==null?0:head1.val;
            val2=head2==null?0:head2.val;
            res.next=new ListNode( (val1+val2+carry)%10 );
            carry=(val1+val2+carry)/10;
            res=res.next;
            head1=head1==null?null:head1.next;
            head2=head2==null?null:head2.next;
        }
        if(carry!=0)res.next=new ListNode(carry);
        return reverseNode(temp.next);
    }
    public ListNode reverseNode(ListNode node){
        if(node.next==null) return node;
        ListNode prev=null;
        ListNode cur=node;
        ListNode curBehind=null;
        while(cur!=null){
            curBehind=cur.next;
            cur.next=prev;
            prev=cur;
            cur=curBehind;
        }
        return prev;
    }
}

力扣刷题篇之链表2_第7张图片

力扣刷题篇之链表2_第8张图片

 就是第一题

/**
 * 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) {
        int carry = 0;
        ListNode ans = new ListNode(0);
        ListNode cur = ans;
        while (l1 != null || l2 != null) {
            int num1 = l1 == null ? 0 : l1.val;
            int num2 = l2 == null ? 0 : l2.val;
            
            int num = num1 + num2 + carry;
            carry = num / 10;
            
            cur.next = new ListNode(num % 10);
            
            l1 = l1 == null ? null : l1.next;
            l2 = l2 == null ? null : l2.next;
            cur = cur.next;
        }
        if (carry == 1)
            cur.next = new ListNode(carry);
        return ans.next;
    }
}

力扣刷题篇之链表2_第9张图片

二、链表的合并

力扣刷题篇之链表2_第10张图片

力扣刷题篇之链表2_第11张图片

很简单的题,注释的代码之所以不行是因为直接用res.next不够准确,要使用一个额外的指针 current 来正确跟踪新链表的尾部。

/**
 * 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 mergeTwoLists(ListNode list1, ListNode list2) {
    //     ListNode node1=list1;
    //     ListNode node2=list2;
    //     ListNode res=new ListNode(0);
    //     if(node1!=null && node2!=null){
    //         if(node1.val < node2.val){
    //             res.next= node1;
    //             node1=node1.next;
    //         }else{
    //             res.next=node2;
    //             node2=node2.next;
    //         }
    //         res=res.next;
            
    //     }
    //     if(node1!=null){
    //         res.next=node1;
    //         // node1=node1.next;
    //     }
    //     if(node2!=null){
    //         res.next=node2;
    //         // node2=node2.next;
    //     }
    //     return res.next;
    // }
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode dummy = new ListNode(0); // 创建虚拟头节点
        ListNode current = dummy; // 当前节点指针

        while (list1 != null && list2 != null) {
            if (list1.val < list2.val) {
                current.next = list1;
                list1 = list1.next;
            } else {
                current.next = list2;
                list2 = list2.next;
            }
            current = current.next;
        }

        // 将剩余的节点连接到结果链表
        if (list1 != null) {
            current.next = list1;
        }
        if (list2 != null) {
            current.next = list2;
        }

        return dummy.next; // 返回真正的头节点
    }

}

力扣刷题篇之链表2_第12张图片

力扣刷题篇之链表2_第13张图片

力扣刷题篇之链表2_第14张图片

利用了分治和递归的方法,将 K 个有序链表分成更小的子问题,然后递归地合并它们。(这题还要再思考下

  1. mergeKLists 方法是入口方法,接受一个 ListNode 数组 lists 作为输入。首先,检查数组的长度,如果为空,直接返回 null。否则,调用 split 方法,将问题分解为更小的子问题。

  2. split 方法用于分治,将 K 个链表分成两部分,然后递归地调用 mergeTwoLists 方法来合并它们。它使用递归分割的方式,将 K 个链表不断分成两半,然后合并两半。

  3. mergeTwoLists 方法用于合并两个有序链表,这个方法在每个分治阶段都会调用。它比较两个链表的头节点的值,选择较小的头节点,然后递归地调用 mergeTwoLists 来合并剩余的部分,最终返回合并后的链表。

采用了分治的方法,将大问题分解为小问题,并逐步合并这些小问题,最终得到合并后的 K 个有序链表。如果输入的 lists 包含 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 mergeKLists(ListNode[] lists) {
        if (lists.length == 0) {
            return null;
        }
        return split(lists, 0, lists.length - 1);
    }

    public ListNode split(ListNode[] lists, int i, int j) {
//        System.out.println(i + " " + j);
        if (j == i) {
            return lists[i];
        }
        int m = (i + j) >>> 1;
        return mergeTwoLists(
                split(lists, i, m),
                split(lists, m + 1, j)
        );
    }

    public ListNode mergeTwoLists(ListNode p1, ListNode p2) {
        if (p2 == null || p1 == null) {
            return p2 == null ? p1 : p2;
        }
        if (p1.val < p2.val) {
            p1.next = mergeTwoLists(p1.next, p2);
            return p1;
        } else {
            p2.next = mergeTwoLists(p1, p2.next);
            return p2;
        }
    }
}

力扣刷题篇之链表2_第15张图片

 

 三、链表中的双指针技巧

力扣刷题篇之链表2_第16张图片

用bigger指向大于或者等于x的链表,smaller指向小于x的链表,最后small连接big。

这里有个地方要注意就是,一定要把最后的节点指向空,避免出现环路。

/**
 * 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 partition(ListNode head, int x) {
        ListNode bigger=new ListNode(0);
        ListNode smaller=new ListNode(0);
        ListNode big=bigger;
        ListNode small=smaller;
        while(head!=null){
            if(head.val

力扣刷题篇之链表2_第17张图片

之前做过的十九题

力扣刷题篇之链表2_第18张图片

力扣刷题篇之链表2_第19张图片

 这里有个特殊情况,只有两个节点,且删除倒数第二节点情况。

用快慢指针,快指针先走n-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 removeNthFromEnd(ListNode head, int n) {

        if (head == null || head.next == null) {
            return null;
        }

        ListNode slow = head;
        ListNode quick = head;
        int count = 0;
        while (count < n -1) {
            quick = quick.next;
            count++;
        }


        ListNode prev = slow;
        boolean notRun = true;
        while (quick != null && quick.next != null) {
            prev = slow;
            quick = quick.next;
            slow = slow.next;
            notRun = false;
        }

        if (notRun) { // 处理只有两个节点,且删除倒数第二节点情况
            head = head.next;
            return head;
        }

        if (prev != slow) {
            prev.next = slow.next;
        }

        return head;

    }
}

力扣刷题篇之链表2_第20张图片

力扣刷题篇之链表2_第21张图片

力扣刷题篇之链表2_第22张图片

 快指针先走一步,每次快指针比慢指针多走一步,如果存在环,两个指针就必然相交,不会相交的就没有环。

/**
 * 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||head.next==null) return false;
        ListNode slow=head;
        ListNode fast=head.next;
        while(slow!=fast && slow!=null && fast!=null && fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        return fast==slow? true:false;
    }
}

 力扣刷题篇之链表2_第23张图片

环形链表的进阶,找入环的第一个节点,这个可以参考左程云算法与数据结构代码汇总之链表(Java)-CSDN博客

力扣刷题篇之链表2_第24张图片

力扣刷题篇之链表2_第25张图片

 快指针比慢指针先走一步,每次快指针都比慢指针多走一步,相遇就是有环,相遇后把快指针重置到头结点,再一起一步步往后,相遇的第一个节点就是入环的第一个节点。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null) {
            return null;
        }

        ListNode slow = head;
        ListNode fast = head;

        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;

            if (slow == fast) {
                // 检测到环路
                fast = head; // 重置 fast 到头节点
                while (slow != fast) {
                    slow = slow.next;
                    fast = fast.next;
                }
                return fast; // 返回环路的起点
            }
        }
        return null; // 没有环路
    }
}

力扣刷题篇之链表2_第26张图片

力扣刷题篇之链表2_第27张图片

简单题,快慢指针,快指针到达末尾的时候,慢指针在中间节点。注意一下,快指针的边界条件就好了。fast!=null && fast.next!=null

/**
 * 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 middleNode(ListNode head) {
        ListNode slow=head;
        ListNode fast=head;
        while(fast!=null && fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        return slow;
    }
}

力扣刷题篇之链表2_第28张图片

力扣刷题篇之链表2_第29张图片

力扣刷题篇之链表2_第30张图片

 先通过快慢指针找到中间节点,把后面的进行翻转,然后再一一连接。

/**
 * 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) {

        ListNode slow = head;
        ListNode fast = head;
        while(fast.next != null && fast.next.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        //反转后半链表
        ListNode tail = slow.next;
        slow.next = null;
        ListNode pre = null;
        while(tail != null){
            ListNode temp = tail.next;
            tail.next = pre;
            pre = tail;
            tail = temp;
        }

        ListNode cur = head;
        while(cur != null && pre != null){
            ListNode cur_next = cur.next, pre_next = pre.next;
            cur.next = pre;
            pre.next = cur_next;
            cur = cur_next;
            pre = pre_next;
            
        }
    }
}

力扣刷题篇之链表2_第31张图片

力扣刷题篇之链表2_第32张图片

 力扣刷题篇之链表2_第33张图片

力扣刷题篇之链表2_第34张图片

 这一题在之前左的链表笔记中也有写。

这里保证了没有环,使两个链表先到同一长度,长的先走他们的长度之差步,之后两个指针一起走,每走一步就对比一下,如果相同,就返回,一直走到最后,还没有就说明不相交。

/**
 * 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) {
        // 题目保证无环
        int n=0;
        ListNode a=headA;
        ListNode b=headB;
        while(a!=null){
            n++;
            a=a.next;
        }
        while(b!=null){
            n--;
            b=b.next;
        }
        ListNode x=headA;
        ListNode y=headB;
        while(n!=0){
            if(n>0){
                x=x.next;
                n--;
            } else{
                y=y.next;
                n++;
            }
        }
        while(x!=null && y!=null){
            if(x==y){
                return x;
            }
            x=x.next;
            y=y.next;
        }
        return null;

    }
}

力扣刷题篇之链表2_第35张图片


总结

终于把链表总结完了,其实也是多敲敲就会了,全是套路啊,反转链表,链表的双指针...有个好现象就是越敲越也舒服了,掌握套路,多敲多练。

力扣刷题篇之链表2_第36张图片

你可能感兴趣的:(算法与数据结构,leetcode,算法,数据结构,java,1024程序员节)