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

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

题目链接: link
视频
文字

1.思路

涉及到头节点指针的修改,所以使用虚拟结点来解决这道题。声明一个指针始终指向要交换的两个结点的前一个结点,这样才能成功交换结点,如果结点数是奇数,则中止条件是cur.next.next=null,若结点数是偶数,则终止条件是cur.next=null。注意在写中止循环条件时应写成while(cur.next=null && cur.next.next=null ),若交换顺序可能会导致空指针异常。
写代码的时候犯了一个小错误,把&&写成了||,造成了空指针异常
时间复杂度:O(n)
空间复杂度:O(1)
Initialization:

A dummy ListNode is created to serve as a placeholder at the beginning of the list, making the swapping process more consistent.
dummy.next = head initializes the dummy’s next pointer to the head of the list.
ListNode cur = dummy initializes a current pointer to start at the dummy node.
Traversing the list:

The while loop continues as long as there are at least two more nodes to swap: while(cur.next != null && cur.next.next != null)
Swapping Process:

ListNode tmp = cur.next temporarily saves the reference to the first node to be swapped.
ListNode tmp2 = cur.next.next.next temporarily saves the reference to the node following the pair to be swapped.
cur.next = tmp.next connects the current node (or dummy node in the first iteration) to the second node of the pair.
cur.next.next = tmp connects the second node of the pair back to the first node, effectively swapping their positions.
tmp.next = tmp2 connects the (originally) first node to the remaining part of the list, or null if there are no more nodes.
cur = cur.next.next moves the current pointer forward by two nodes for the next iteration.
Return Value:

return dummy.next returns the new head of the list (ignoring the dummy node).

2.代码实现

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummyNode= new ListNode(0,head);
        ListNode cur=dummyNode;
        while(cur.next!=null&&cur.next.next!=null){//这个地方第一次写写成了||造成了空指针异常
            //执行换结点操作 cur指向被换两个结点的前一个结点
            ListNode tmp=cur.next;//交换的第一个结点的地址
            ListNode tmp1=cur.next.next.next;//交换的第二个结点的后一结点
            cur.next=cur.next.next;
            cur.next.next=tmp;
            tmp.next=tmp1;
            cur=cur.next.next;
        }
        return dummyNode.next;

    }
}

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

题目链接: link
视频
文字

1.思路

自己的思路:直接总结出规律,倒数第n个结点就是第size-n+1个结点。但前提是要知道链表结点的,需要先提前遍历一遍得知结点总个数后,再遍历到size-n处进行删除结点操作。
carl思路:用双指针的话一遍扫描即可结束。

双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾,此时slow指向要删除的结点。
使用虚拟头结点,这样方便处理删除实际头结点的逻辑
step1:fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作)
step2:fast和slow同时移动,直到fast指向末尾
step3:删除slow指向的下一个节点

2.代码实现

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        //涉及到头节点所以用虚拟结点
        ListNode dummyNode=new ListNode(0,head);
        ListNode fast=dummyNode;
        ListNode slow=dummyNode;
        n++;
        while(n--!=0&&fast!=null){
            fast=fast.next;
        }
        while(fast!=null){
            fast=fast.next;
            slow=slow.next;
        }
        slow.next=slow.next.next;
        return dummyNode.next;
    }
}

19.面试题 02.07. 链表相交

题目链接: link
文字

1.思路

简单来说,就是求两个链表交点节点的指针。 这里要注意,交点不是数值相等,而是指针相等
在这里插入图片描述
在这里插入图片描述
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。否则循环退出返回空指针。

2.代码实现

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int lengthA=0, lengthB=0;
        ListNode cur=headA;
        ListNode curB=headB;
        //计算两个链表的长度
        while(cur!=null){
            lengthA++;
            cur=cur.next;
        }
        cur=headB;
        while(cur!=null){
            lengthB++;
            cur=cur.next;
        }
        //链表A长
        cur=headA;
        if(lengthA>=lengthB){
            int gap=lengthA-lengthB;
            while(gap--!=0){
                cur=cur.next;
            }
            while(cur!=null){
                if(cur==curB)//注意这里比较的不是val,而是指针指向的地址,所以不会有分叉的情况
                    return cur;
                else{
                    cur=cur.next;
                    curB=curB.next;
                }
            }
            return null;
        }
        //链表B长
        else{
            int gap=lengthB-lengthA;
            while(gap--!=0){
                curB=curB.next;
            }
            while(cur!=null){
                if(cur==curB)//注意这里比较的不是val,而是指针指向的地址,所以不会有分叉的情况
                    return cur;
                else{
                    cur=cur.next;
                    curB=curB.next;
                }
            }
            return null;
        }
    }
}

19.142.环形链表II

题目链接: link
视频
文字

1.思路

主要考察两知识点:
判断链表是否环
如果有环,如何找到这个环的入口

1.判断链表是否有环

可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
为什么fast 走两个节点,slow走一个节点,有环的话,一定会在环内相遇呢,而不是永远的错开呢
首先第一点:fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇,这是毋庸置疑的。
为什么fast指针和slow指针一定会相遇呢?
fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。

2.如果有环,如何找到这个环的入口

在这里插入图片描述
在这里插入图片描述
也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。
让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。
那么 n如果大于1是什么情况呢,就是fast指针在环形转n圈之后才遇到 slow指针。
其实这种情况和n为1的时候 效果是一样的,一样可以通过这个方法找到 环形的入口节点,只不过,index1 指针在环里 多转了(n-1)圈,然后再遇到index2,相遇点依然是环形的入口节点。

写代码时犯了一个错误,在判断是否有环的循环里,先判断了fast==slow,事实上应该先移动快慢指针,再判断在哪一个结点相交。

2.代码实现

public class Solution {
    public ListNode detectCycle(ListNode head) {
        //双指针判断是否有环
        ListNode slow=head;
        ListNode fast=head;
        while(fast!=null&&fast.next!=null)//因为fast移两个结点,一定要保证fast的后一个结点不为空,否则会空指针异常
        {
            slow=slow.next;
            fast=fast.next.next;
            //确认有环
            if(fast==slow){
                //寻找入环口
                ListNode index=slow;
                ListNode index1=head;
                while(index!=index1){
                    index=index.next;
                    index1=index1.next;
                }
                //找到index=index1 即入环口处
                return index1;
            } 
        }
        return null;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值