【Leetcode】-代码随想录算法训练营Day4 | 24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II

Leetcode题目-24. Swap Nodes in Pairs

链接: 24. Swap Nodes in Pairs

思路

此题主要还是对虚拟头节点的运用。经过简单的画图可以了解,两两交换过程中,对头两个节点的处理和其他节点的处理是略有不同的。具体如下图。
头两个节点的处理:
【Leetcode】-代码随想录算法训练营Day4 | 24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II_第1张图片
其他节点的处理:
【Leetcode】-代码随想录算法训练营Day4 | 24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II_第2张图片
可以看出来如果交换的是头两个节点,其步骤少了一步。所以这道题用虚拟节点可以简化这一问题。
做题的时候注意遍历循环的条件。

代码实现

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummyHead = new ListNode(0, head);
        // 因为这里一直有cur=pre.next,也可以将pre和cur合成一个变量
        // 这里方便理解,可以看作同步移动的双指针,所以设置了两个变量
        ListNode pre = dummyHead;
        ListNode cur = head;
        ListNode next;
        // 如果上文只有pre一个变量,则这里的循环条件是
        // pre.next!=null && pre.next.next!=null
        while (cur != null && cur.next != null) {
            next = cur.next;
            pre.next = cur.next;
            cur.next = next.next;
            next.next = cur;

            pre = cur;
            cur = pre.next;
        }

        return dummyHead.next;
    }

总结

时间复杂度:O(n)
空间复杂度:O(1)
此题关键是要意识到头节点的特殊性,并通过设置虚拟头节点将特殊的头节点转换为统一性处理,大大简化代码。
代码随想录文章讲解:传送门
代码随想录视频讲解:传送门

Leetcode题目-19. Remove Nth Node From End of List

链接: 19. Remove Nth Node From End of List

思路

初看这题,傻傻的我,先将链表遍历了一遍,求出长度,然后再算出最后第n个是顺数第几个,再遍历一遍查找到,然后删除。
这里可以使用双指针法,设定快慢两个指针,快指针从头节点先移动n个节点,慢指针指向头节点,然后两个指针同时向后遍历,当快指针到达链表末尾的时候,慢指针就刚好指向的是倒数第n个节点啦,然后去执行删除操作。
还需注意的是,在删除节点的操作上,删除头节点和删除非头节点的处理又有差别,所以这里还是可以用到虚拟头节点来统一操作。在链表的题目里,都需要想一下头节点是否特殊,如果特殊可以考虑使用虚拟头节点。

代码实现

这里我的两种解法都贴一下:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode currentNode = head;
        int count = 0;
        while (currentNode != null) {
            count++;
            currentNode = currentNode.next;
        }

        ListNode dummyHead = new ListNode(0, head);
        currentNode = dummyHead;
        for (int i = 0; i < count - n; i++) {
            currentNode = currentNode.next;
        }
        currentNode.next = currentNode.next.next;

        return dummyHead.next;
    }
}
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummyHead = new ListNode(0,head);
        ListNode fast = dummyHead, slow = dummyHead;
        for(int i = 0; i < n; i++){
            fast = fast.next;
        }

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

        slow.next = slow.next.next;
        return dummyHead.next;
    }
}

总结

时间复杂度:O(n)
空间复杂度:O(1)
此题关键是要意识到头节点的特殊性,并通过设置虚拟头节点将特殊的头节点转换为统一性处理,大大简化代码。
代码随想录文章讲解:传送门
代码随想录视频讲解:传送门

Leetcode题目-面试题 02.07. Intersection of Two Linked Lists LCCI

链接: 面试题 02.07. Intersection of Two Linked Lists LCCI

思路

这题需要注意一点,“Note that the intersection is defined based on reference, not value.”题目中说了,相交的交点,是引用相同而非值相同,即交点是同一个节点,而非值一样的两个节点。所以两个链表交点后的后续节点都是一模一样的。
如果两个链表一长一短,同时从头节点开始遍历,那么他们到达交点的循环轮次肯定不一样,但是如果两个链表长度相同,那么他们到达交点时,循环轮次是一样的。

  1. 从上一题可以得到灵感,设两个链表长度为m和n且m>n,如果我将长链表从第m-n个节点开始遍历,n从头节点开始遍历,两个链表就会同时到达交点。
    按照这个思路,我们可以写出代码实现,但是要注意两点,我们拿到的两个链表A和B,我们并不知道哪个长哪个短,需要做一下判断和设定,再决定哪个链表要先走几步。
  2. 还有一种思路,设两个链表A和B长度分别为m和n,相交的链表长度为k(即交点后的链表长),设定两个指针a和指针b:
    • a指针遍历完链表A,指向链表的B的头节点开始遍历链表B,当a指针经由链表B到达交点时,它走过的路程为:m+(n-k);
    • b指针遍历完链表B,指向lianbiaoA的头节点开始遍历链表A,当b指针经由链表A到达交点时,它走过的路程为:n+(m-k);
      也就是说这时候他们会同时到达交点!所以按上述规则这样遍历后,当指针a和b达到的节点相同时,这个点就是交点。
      【Leetcode】-代码随想录算法训练营Day4 | 24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II_第3张图片

代码实现

方法一

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int lenA = 0, lenB = 0;
        ListNode curA = headA, curB = headB;
        while (curA != null) {
            curA = curA.next;
            lenA++;
        }
        while (curB != null) {
            curB = curB.next;
            lenB++;
        }

        curA = headA;
        curB = headB;
        // 让curA为最长链表的头,lenA为其长度
        if (lenB > lenA) {
            //1. swap (lenA, lenB);
            int tmpLen = lenA;
            lenA = lenB;
            lenB = tmpLen;
            //2. swap (curA, curB);
            ListNode tmpNode = curA;
            curA = curB;
            curB = tmpNode;
        }

        for (int i = 0; i < lenA - lenB; i++) {
            curA = curA.next;
        }

        while (curA != null) {
            if (curA == curB) return curA;
            curA = curA.next;
            curB = curB.next;
        }
        return null;
    }
}

方法二

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA;
        ListNode curB = headB;
        while (curA != curB) {
            if (curA == null) curA = headB;
            else curA = curA.next;
            if (curB == null) curB = headA;
            else curB = curB.next;
        }
        return curA;
    }
}

总结

此题结合图理解会清晰很多,双指针法的灵活运用。
时间复杂度:O(n + m)
空间复杂度:O(1)
代码随想录文章讲解:传送门

Leetcode题目-142. Linked List Cycle II

链接: 142. Linked List Cycle II

思路

这一题的环形可以从上题得到一点启发,因为本题只有一个链表,如果把指针A和指针B都看作是在这个链表内行走,但是行走的步长不同,即快慢指针。同样结合图来理解会更容易。
这里用一下carl的图:
设快指针一次前进两个节点,慢指针一次前进一个节点。
【Leetcode】-代码随想录算法训练营Day4 | 24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II_第4张图片
当两个节点相遇时,快指针走过的路程为x+n(y+z)+y,慢指针走过的路程为x+y。这里可以这样理解,两个人赛跑,一个快一个慢,那当他们两个遇见的时候,肯定是慢的那个被套圈了。
将节点前进的步长看作速度,可以得到x+n(y+z)+y = 2(x+y),因为我们要得到环的入口,所以需要求x,得到x的表达式,由上可得,x=n(y+z)-y=(n-1)(y+z)+z,这意味着,当快慢指针相遇时,只要慢指针再走x步就能到达环形入口节点(不管是不是第一次到),而此时如果也有一个节点从头节点出发,每次也前进一个节点,那么它和慢节点会在环形入口节点相遇。

代码实现

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head,slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow){
            	// 这里index2等于fast和slow都一样
            	// 因为它们此时相遇了,指向同一个节点
                ListNode index1 = head, index2 = fast;
                while(index1 != index2){
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}

总结

对于查找倒数第几个节点、查找环入口、查找交点等链表问题,都要考虑用双指针法解决问题,以上几个题都是非常典型的链表中的双指针运用。解题过程可以多画图模拟,有助理解。
代码随想录文章讲解:传送门
代码随想录视频讲解:传送门

你可能感兴趣的:(Leetcode-代码随想录,链表,算法,leetcode,数据结构,java)