【力扣刷题】Day04——链表专题

文章目录

    • 4. 两两交换链表中的节点
    • 5. 删除链表的倒数第N个节点
    • 6. 链表相交
    • 7. 环形链表II
    • 8. 删除排序链表中的重复元素
    • 9. 删除排序链表中的重复元素 II
    • 10. 奇偶链表


上一篇文章:【力扣刷题】Day03——链表专题_塔塔开!!!的博客-CSDN博客

4. 两两交换链表中的节点

题目链接:24. 两两交换链表中的节点 - 力扣(LeetCode)

思路:由于是一对一对的进行交换,因此我们枚举的时候也要一对一对的来。

尤其要注意

  • 交换的顺序
  • 头节点也发生了修改,因此加入虚拟头节点

【力扣刷题】Day04——链表专题_第1张图片

Code

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;

        // 一对一对的枚举
        for(ListNode p = dummy; p.next != null && p.next.next != null;){
            ListNode a = p.next;
            ListNode b = a.next;

            // 交换
            p.next = b;// 1
            a.next = b.next;// 2
            b.next = a;// 3
            // 指针移动,为下一次做好准备
            p = a;// 4
        }
        return dummy.next;
    }
}

5. 删除链表的倒数第N个节点

题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

要删除节点就要找到要删除节点的前驱在哪:

思路一:枚举找到要删除节点的前驱

【力扣刷题】Day04——链表专题_第2张图片

当头节点会改变时我们引入虚拟头节点

Code

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
       
       ListNode dummy = new ListNode(-1);
       dummy.next = head;

       int len = 0, cnt = 0; 
       for(ListNode cur = dummy; cur != null; cur = cur.next) len ++;
       for(ListNode cur = dummy; cur != null; cur = cur.next){
           cnt ++;
           if(len - n == cnt){// 找到倒数第n+1得位置
               cur.next = cur.next.next;
               break; 
           }
       }
       return dummy.next;

    }
}

思路二:双指针优化:双指针找到要删除节点的前驱

上述得关键是要找到删除节点得前一个节点,采用的是遍历的方式,现在我们用双指针进行优化:

1、创建虚拟结点dummydummy指向head.next
2、指针firstsecond初始化均指向dummyfirst指针走n步,first,second指针同时向后走,直到first走到末尾时终止
3、这样找到了删除结点的前一个结点,让前一个结点指向删除结点的后一个结点即可

【力扣刷题】Day04——链表专题_第3张图片

Code

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;

        ListNode first = dummy;
        ListNode second = dummy;

        for(int i = 0; i < n; i ++) first = first.next;// first先往后走n步
        // 注意:这里是first.next != null 而不是first
        // 因为当指针走到最后一个节点了,first它不为空,还会再执行一次循环体,这样就不对了
        while(first.next != null){
            first = first.next;
            second = second.next;
        }
        second.next = second.next.next;

        return dummy.next;
    }
}

6. 链表相交

题目链接:160. 相交链表 - 力扣(LeetCode)

一种非常巧妙得思路,分别给链表A和链表B设置指针A和指针B,然后开始遍历链表,如果当前链表走完了,则将指针指向另一个链表得头部继续遍历,直到两个指针相遇。

两个指针走过得路径长度分别为:

  • 指针A:a + b + c
  • 指针B:a + b + c
  • 明显它们走过得路径长度是一样的,即相遇的节点

两个链表不相交:
在这里插入图片描述

a,b 分别代表两个链表的长度,则两个指针分别走 a+b 步后都变成 null,最终null相等

两个链表相交:

【力扣刷题】Day04——链表专题_第4张图片

则两个指针分别走 a+b+c 步后在两链表交汇处相遇。

Code

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p = headA;
        ListNode q = headB;

        while(p != q){
            if(p == null) p = headB;
            else p = p.next;

            if(q == null) q = headA;
            else q = q.next;
        }
        return p;
    }
}

7. 环形链表II

题目链接:142. 环形链表 II - 力扣(LeetCode)

解法一:哈希判重,入口即为第一次重复的节点

空间复杂度:O(n)

Code

/**
 * 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) {
        Set<ListNode> set = new HashSet<ListNode>();
        for(ListNode p = head; p != null; p = p.next){
            if(set.contains(p)) return p;
            set.add(p);
        }
        return null;
    }
}

解法二:快慢指针

空间复杂度:O(1)

思路:使用快慢指针,快指针每次走两步,满指针每次走一步,当两个指针第一次相遇的时候,将其中一个指针(快)回到起点,然后快慢指针一起每次走一步,再次相遇即为环的入口。
【力扣刷题】Day04——链表专题_第5张图片

假设C点为两个指针的相遇点

第一次相遇:

  • 慢指针:x + y
  • 快指针:x + y + n(y + z),快指针可能转了好几圈

不妨直接设快指针转了1圈,由2倍得出等式:

2(x + y) = x + y + (y + z)

化简得:x = z

而此时又是在第一次相遇的点(C),将其中一个指针(快)回到起点,然后快慢指针一起每次走一步,再次相遇即为环的入口(距离都是一样的,都为z)。

详细解答证明:把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表II_哔哩哔哩_bilibili

Code

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;

        // 快慢指针用起点开始,快走两步,慢走一步
        // 当快慢第一次相遇时,快回到起点,然后快慢一起走一步,再次相遇时即为环的入口
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;

            if(slow == fast){// 第一次相遇
                fast = head;
                while(fast != slow){
                    fast = fast.next;
                    slow = slow.next;
                }
                // 第二次相遇即为环的入口
                return fast;
            }
        }    
        return null;
    }
}

8. 删除排序链表中的重复元素

题目链接:83. 删除排序链表中的重复元素 - 力扣(LeetCode)

Code

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode p = head;
        while(p != null){
            if(p.next != null && p.val == p.next.val) p.next = p.next.next;
            else p = p.next;
        }
        return head;
    }
}

9. 删除排序链表中的重复元素 II

题目链接:82. 删除排序链表中的重复元素 II - 力扣(LeetCode)

这题与上一题的区别是,一旦元素出现重复就要全部删除掉,而上一题要保留一个

之前我们是一个一个的删除,现在我们要对连续一段(相同值)进行删除,通过双指针实现:

【力扣刷题】Day04——链表专题_第6张图片

由于可能要删除头节点,因此设置虚拟节点!

时间复杂度:O(n)

空间复杂度:O(1)

Code

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;

        ListNode p = dummy;
        while(p.next != null){
            ListNode q = p.next;
            while(q.next != null && q.val == q.next.val) q = q.next;
            if(q == p.next) p = p.next;// 说明没有重复节点 p移动
            else p.next = q.next;// 删除重复区间的节点
        }
        return dummy.next;
    }
}

10. 奇偶链表

题目链接:328. 奇偶链表 - 力扣(LeetCode)

思路:先分离后合并,双指针,交替移动即可。

【力扣刷题】Day04——链表专题_第7张图片

  • 时间复杂度:O(n)

  • 空间复杂度:O(1)

Code

class Solution {
    public ListNode oddEvenList(ListNode head) {
        if(head == null) return head;

        ListNode ji = head;
        ListNode ou_head = head.next;
        ListNode ou = ou_head;

        // 奇偶交替拼接,最后两个链再拼一起
        while(ou != null && ou.next != null){
            ji.next = ou.next;
            ji = ou.next;
            ou.next = ji.next;
            ou = ji.next;
        }
        // 将奇偶链拼接
        ji.next = ou_head;
        return head;
    }
}

你可能感兴趣的:(代码随想录力扣刷题)