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

95 阅读4分钟

24.两两交换链表中的节点代码随想录 (programmercarl.com)24. 两两交换链表中的节点 - 力扣(LeetCode)

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:

这个问题先明确一点,是两两交换,所以当链表节点数为奇数个时,最后一个节点应当是不动的,也就是链表只有一个数据时,链表不动,所以在遍历时,需要加上一个判断当前节点下一个节点和下下一个节点是否为空的判断。

另外,要明确交换的思路,首先为了方便操作头节点,所以我们引入一个虚拟头节点。首先将当前节点的下一个节点设为下下一个节点,然后将当前节点的下下一个节点设为原下一个节点,再将当前节点的下下下一个节点设为原下下下一个节点,形象点可以如图所示

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

实现如下:

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode vHead=new ListNode(0,head);
        ListNode cur=vHead;
        ListNode temp=new ListNode();
        ListNode temp1=new ListNode();
        while(cur.next!=null&&cur.next.next!=null){
            temp=cur.next;
            temp1=cur.next.next.next;
            cur.next=cur.next.next;
            cur.next.next=temp;
            cur.next.next.next=temp1;
            cur=cur.next.next;
        }
        return vHead.next;
    }
}

这里在调试的时候出现了问题,我原先将return的数据设为head,却发现少了一个数据,后来发现,head在遍历的过程中已经发生了变化,所以这里我们返回的头节点应当是虚拟头节点的下一个节点。

19.删除链表的倒数第N个节点代码随想录 (programmercarl.com)19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

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

这题的思路也比较简单,使用双指针的思路,一个快指针一个慢指针,在开始时快慢指针都指向虚拟头节点,然后快指针向后移动一个节点循环N次,接下来快慢指针一起动,直到快指针指向最后一个节点,此时慢指针的下一个节点即为要删除的节点。实现如下:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {

        ListNode vHead=new ListNode(0,head);
        ListNode fast=vHead;
        ListNode slow=vHead;
        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 vHead.next;
    }
}

这题和上题又犯了一样的错误:返回了head,应当返回虚拟头节点的下一个节点。

链表相交代码随想录 (programmercarl.com)面试题 02.07. 链表相交 - 力扣(LeetCode)

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

这题如果没仔细理解题意,可能会从最后一个节点开始向前比较,直到不相等,但是这是两个可能相交的链表,只要其中一个节点相同,那么后续的所有节点都肯定相同,所以我们可以将两个长度可能不等的链表尾部对齐,然后从短一点的链表开始向后遍历寻找相同节点,找到的则为相交节点。代码如下:

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int len1=0,len2=0;
        ListNode cur1=new ListNode(0);
        ListNode cur2=new ListNode(0);
        cur1=headA;
        while(cur1!=null){
            len1++;
            cur1=cur1.next;
        }
        cur2=headB;
        while(cur2!=null){
            len2++;
            cur2=cur2.next;
        }
        if(len1>len2){
            int temp1;
            ListNode temp2=new ListNode(0);
            
            temp1=len1;
            len1=len2;
            len2=temp1;
            
            temp2=headA;
            headA=headB;
            headB=temp2;
        }
        cur1=headA;
        cur2=headB;
        for(int i=0;i<len2-len1;i++){
            cur2=cur2.next;
        }
        while(cur1!=cur2&&cur1!=null){
            cur1=cur1.next;
            cur2=cur2.next;
        }
        if(cur1==null)
            return null;
        return cur1;
    }
}

142环形链表代码随想录 (programmercarl.com)

142. 环形链表 II - 力扣(LeetCode)

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。 不允许修改 链表。 

这题一开始没什么思路,看了眼代码随想录,才想到只要确定一个在环里的节点,就能够计算出环的长度,也可以找到环的起始节点在哪里。使用快慢指针,快指针每次走两个节点,慢指针一个节点,这样快慢指针相遇的时候必定在环内(或者没有环),即可确定环内一节点。求出环长后从第一个节点开始快指针先走长度减一格,再快慢指针同时移动,直到快指针的下一个节点为慢指针,此时慢指针即为入环的第一个节点。

代码如下:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast=head,slow=head;
        if(head==null)
            return null;
        while(true){
            if(fast.next==null)
                return null;
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow)
                break;
            else if(fast==null)
                return null;
        }
        //此时slow已在环中,所以我们进一步求出环中节点数量
        int leng=0;
        ListNode cur=slow;
        while(true){
            leng++;
            cur=cur.next;
            if(cur==slow)
                break;
        }
        fast=head;
        slow=head;
        for(int i=0;i<leng-1;i++){
            fast=fast.next;
        }
        while(fast.next!=slow){
            fast=fast.next;
            slow=slow.next;
        }
        return slow;
    }
}

===

总结:题目稍难,但是总体还是多用快慢指针的思想。