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

力扣题目链接(opens new window)

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

代码随想录算法训练营第四天|24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II、总结_快慢指针

思路:

代码随想录算法训练营第四天|24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II、总结_双指针_02

按照上述顺序,去记录防止存在某个节点意外失踪。

/**
 * 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 swapPairs(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode dummy = new ListNode(-1, head);
        ListNode cur = dummy;
        while(cur.next != null && cur.next.next != null){
            ListNode tmp = cur.next;
            ListNode tmp1 = cur.next.next.next;

            cur.next = cur.next.next;
            cur.next.next = tmp;
            cur.next.next.next = tmp1;

            cur = cur.next.next;
        }
        return dummy.next;
    }
}

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

力扣题目链接(opens new window)

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

进阶:你能尝试使用一趟扫描实现吗?

示例 1:

代码随想录算法训练营第四天|24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II、总结_快慢指针_03

输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5] 示例 2:

输入:head = [1], n = 1 输出:[] 示例 3:

输入:head = [1,2], n = 1 输出:[1]

思路:

双指针方法,最后删除的节点,一定要知道它的前一个节点才能删除。而n可以转化为两个指针之前的距离。

/**
 * 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) {
        ListNode dummy = new ListNode(-1,head);
        ListNode pre = dummy;
        ListNode cur = head;
        for(int i = 0; i < n; i++){
            cur = cur.next;
        }
        while(cur != null){
            pre = pre.next;
            cur = cur.next;
        }
        pre.next = pre.next.next;
        return dummy.next;
    }
}

面试题 02.07. 链表相交

同:160.链表相交

力扣题目链接(opens new window)

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

代码随想录算法训练营第四天|24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II、总结_链表_04

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

示例 1:

代码随想录算法训练营第四天|24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II、总结_链表_05

示例 2:

代码随想录算法训练营第四天|24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II、总结_链表_06

示例 3:

代码随想录算法训练营第四天|24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II、总结_快慢指针_07

代码随想录算法训练营第四天|24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II、总结_快慢指针_08

思路:

一开始的思路真不太好想,想了十分钟看了眼题解的开头。要想知道是否有相交的节点,需要先将两个链表的长度差给抹去,较长的指针与较短的首指针保持同样的位置。如果依然节点不一致,就去往下找寻headA == headB的情况,需要注意的是,如果找到最后,headA == null和headB == null都没能找到headA == headB的情况,就说明两者没有相交的节点。

/**
 * 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 lenA = 0;
        int lenB = 0;
        ListNode cura = headA;
        ListNode curb = headB;
        while(cura != null){
            lenA++;
            cura = cura.next;
        }
        while(curb != null){
            lenB++;
            curb = curb.next;
        }

        if(lenA > lenB){
            int n = lenA-lenB;
            while(n-- > 0){
                headA = headA.next;
            }
            while(headA != headB && headA != null && headB != null){
                headA = headA.next;
                headB = headB.next;
            }
            if(headA != null){
                return headA;
            }
            return null;
        }else{
            int n = lenB-lenA;
            while(n-- > 0){
                headB = headB.next;
            }
            while(headA != headB && headA != null && headB != null){
                headA = headA.next;
                headB = headB.next;
            }
            if(headA != null){
                return headA;
            }
            return null;
        }

    }
}

142.环形链表II

力扣题目链接(opens new window)

题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

代码随想录算法训练营第四天|24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II、总结_链表_09

思路:

这题一刷做到的时候,想了半天,需要运用到一定的数学思维,不能理解或者理解不了。二刷只想到了要用快慢指针判断是否有环,慢指针一次移动一步,快指针一次移动两步,如果快指针不存在null,并且最终slow和fast相等,则说明会存在环。

找到判断环之后,我去计算入口节点的时候出现问题,误以为快慢指针相遇的节点就是环的入口,导致计算超时。看了题解才发现,计算环入口节点才是重头戏。

代码随想录算法训练营第四天|24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II、总结_链表_10

如图,x表示从开头到入口的距离,z表示入口到快慢指针相遇的节点,y表示相遇节点到入口的距离。那么存在以下关系:

slow移动 (x+z), fast 移动 x+z+n(y+z),以及 2(x+z) = x+z+n(y+z) => x = (n-1)(y+z)+y , 其中n表示fast指针在环内绕的圈数,把n-1的环内距离抵消掉的话,就可以推导出x = y,放两个指针同时出发,相遇的地方就是入口

/**
 * 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) {
        //快慢指针,快指针比慢指针快两步,这样如果存在环,快慢指针一定会相遇
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){    //确定有环
                ListNode index1 = head;
                ListNode index2 = fast;
                while(index1 != index2){
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}

总结:

双指针和虚拟头节点是最主要方法,理清楚链表之间联系,可以解决很多问题,相比一刷能直接AC的题变多了,还是很欣慰的,这是个明显的进步。