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

(〇)理论

1.头结点:
使用虚拟头结点dummyHead的好处是:可以对第一个结点“一视同仁”,不用判断并单独处理第一个结点

(一) 24. 两两交换链表中的节点

用虚拟头结点,这样会方便很多。

题目链接/文章讲解/视频讲解

1. 思路

我将一次while循环中的操作叫做一组。关于一组操作的划分:设当前结点为cur,起始时cur指向虚拟的头结点,此时需要进行调换位置的是,第一个、第二个有内容的结点。调换完之后变成了新的第一个、新的第二个节点,此时cur应指向新第二个结点,以此来进行第三个、第四个结点的调换。

while的终止条件呢?如果cur后没有结点,或只有一个结点都是不需要调换的,即while能运行下去的条件是while(cur.next != null && cur.next.next != null) 注意是*&&,A或B的否命题是非A且非B*

力扣上的链表都是没有虚拟头结点的,所以如果想用的话,要自己设置一个。

2. 代码

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;//虚拟头结点创建完毕

        ListNode cur = dummyHead;//工作结点
        ListNode temp2;//cur后的第2个结点
        ListNode temp3;//cur后的第三个结点,防走丢

        while(cur.next != null && cur.next.next != null){
            temp3 = cur.next.next.next;
            temp2 = cur.next.next;
            //把2插入 cur和1之间
            temp2.next = cur.next;
            cur.next = temp2;
            //新2 与 旧3 连接
            temp2.next.next = temp3;
            //更新 cur
            cur = temp2.next;
        }
        return dummyHead.next;

    }
}

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

双指针的操作,要注意,删除第N个节点,那么我们当前遍历的指针一定要指向 第N个节点的前一个节点

题目链接/文章讲解/视频讲解

1. 思路

采用快慢指针的思想。
首先,明确 要删除倒数第n个结点,那么查找的指针要落在倒数第n+1个结点上,这样才好删除倒数第n个。
其次,如何让一个指针准确的落在n+1呢?我们目前可以准确落的位置包括:倒数第0个(从头遍历到null时)。所以可以与a指针保持n+1个距离发出一个b指针,当a指针到达最末null位置时,b指针刚好到达倒数n+1的位置上。
最终,可以开始删除了。

快指针a先出发,慢指针b延后n+1个身位再出发。
直到快指针a为null时停止。开始进行删除操作。

2. 代码

public ListNode removeNthFromEnd(ListNode head, int n) {
    //先自己加一个虚拟头结点
    ListNode dummyHead = new ListNode();
    dummyHead.next = head;

    //设置快慢指针
    ListNode fast = dummyHead, slow = dummyHead;
    int count = 0;//计数 看fast已走了几步
    while(fast != null){
        fast = fast.next;
        count++;
        if(count > n + 1){//1
            slow = slow.next;
        }
    }
    slow.next = slow.next.next;
    return dummyHead.next;//2
}

3. 实现过程中的问题

1.注意何时slow开始走 是count > n + 1
2.最后返回的是dummy.next,因为有可能原本的head头结点也要被删除。!

(三) 面试题 02.07. 链表相交

链表相交,指有结点地址相同。而非数值相同
并且由于每个结点只有一个next指针,所以一旦相交,后面就一直重合。

题目链接/文章讲解

1. 思路

链表1在相交之前的长度为m,链表2在相交之前的长度为n,剩余的两者重合的链表长度为a。m + a + n == n + a + m。指针p遍历链表1,结束后,去遍历链表2;指针q遍历链表2,结束后去遍历1;这样当指针p走过m + a + n时,指针q也走过了n + a + m;两指针刚好相遇。
若两个链表没有交点呢?!指针p走过m+n ,指针q走过n+m,两个指针都会指向null,还是会相等的。

2. 代码

public class Solution {
	public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
	    ListNode p = headA;
	    ListNode q = headB;
	
	    //链表有相交,最终在相交的结点处,while会停止;若无交点,最终两个指针都会指向null,也会相等
	    while(p != q){
	        //p不为null 就next,p为null 就切到headB;q同理
	        p = p == null ? headB : p.next;
	        q = q == null ? headA : q.next;
	    }
	    return p;
	}
}

(四) 142.环形链表II

算是链表比较有难度的题目,需要多花点时间理解 确定环和找环入口。

题目链接/文章讲解/视频讲解

1. 思路

1.1 直观思路

遍历;检查是否在set中已有该结点,若有返回;否则,将刚遍历过的结点存入哈希set中

1.2 其他思路

使用双指针法。其他可以使用双指针法题目还包括:寻找距离尾部第 K 个节点、寻找环入口、寻找公共尾部入口等。

1.指针第一次相遇
设置fast,slow 两个指针均指向head。fast每次走两步,slow每次走一步。=>f = 2s (1)
1)若无环,则fast会走到null,此时停止即可
2)若有环。
fast先进入环。当slow进入环后,相对slow而言,fast每次都会接近slow一步,最终 fast会追上slow。
设在环的入口前有结点a个,环的结点数b个。在fast与slow在环中相遇时。fast已经走过f = s + nb(2)(f除了走过s走过的路,还一直在环里转圈等b,所以多走了nb)
根据式(1) f=2s,式(2) f = s + nb 可以解出(3) s = nb; (4)f = 2nb

2.分析
再想一下,若从头结点开始走,走到环入口位置的指针都是走多少步呢?k = a + nb。s指针已经走了nb了,再走a步就会到达入口处。但我们却不知道a是多少,如何找到帮我们衡量a长度的方法呢?想到a的定义 本身就是从头结点到入口处的距离。所以此时让fast结点指向head,与slow一起走a步就会 一起到达入口处!

2. 代码

1.set

public class Solution {
    //返回链表开始入环的第一个节点
    public ListNode detectCycle(ListNode head) {
        HashSet<ListNode> set = new HashSet<>();
        while(head != null){
            if(set.contains(head)){
                return head;
            }
            set.add(head);
            head = head.next;
        }
        return null;
    }
}

时间复杂度 O(n)
空间复杂度O(n)

2.双指针

public ListNode detectCycle1(ListNode head) {
    ListNode fast = head;
    ListNode slow = head;

    while(fast != null && fast.next != null){
        fast = fast.next.next;
        slow = slow.next;
        //两指针相遇时,就要将fast指向head了
        if(fast == slow){
            fast =  head;
            while (fast != slow){
                fast = fast.next;
                slow = slow.next;
            }
            //两指针相遇了 到达环的入口了
            return fast;
        }
    }

    //只有fast == null || fast.next == null 才会到这 说明没环
    return null;
}

时间复杂度O(n)//通过慢指针考虑
空间复杂度O(1)

(五) 今日收获

关于链表的问题。经常使用到的是双指针法。
包括但不限于:寻找距离尾部第 K 个节点、寻找环入口、寻找公共尾部入口等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值