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

今日题目:

  • 24.两两交换链表中的节点
  • 19. 删除链表的倒数第N个节点
  • 面试题02.07 链表相交
  • 142. 环形链表Ⅱ

解题思想:

  1. 虚拟头结点

在24. 两两交换链表中的节点此题中,同样需要设置虚拟头结点,它指向的是需要进行交换的两个节点的前一个位置。这样就可以用它来控制两个节点的交换。
需要注意循环的结束条件:因为cur指针指向的是两个交换节点的前一个节点位置,所以当cur指针后面没有节点,或者只有一个节点,都是循环停止的条件,即!cur.next || !cur.next.next

  1. 删除链表节点

第19题删除链表的倒数第n个节点,此题的关键点是如何找到要删除的节点的前一个位置。删除第n个节点,操作指针一定要指在第n-1个节点位置上
利用双指针,快指针先走n步,然后快慢指针同时走,直到快指针指向null停止,此时慢指针停在的位置就是倒数第n个节点,但是我们需要的是慢指针停在第n-1个节点,所以快指针应该在最开始多走一步,走n-1个位置。

  1. 链表相交

想要找出链表之间开始相交的位置,需要先计算出两条链表的长度,再让它们尾部对齐,再逐个对比节点所在的值是否相等。
题目不难,但是可以使用一些小技巧让代码更优雅:计算链表的这部分逻辑可以抽离出去,写一个函数专门用来计算链表长度;再者,为了使得代码逻辑不重复,可以让链表A永远是比较长的链表,如果不是的话就交换,这样操作链表对齐的时候只需要对A链表进行指针移动。

  1. 判断链表是否有环

使用快慢指针,分别定义fast和slow指针,从头结点触发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果fast和slow指针在中途相遇,则说明链表有环。
因为如果有环,fast指针必定先进入环中,如果fast指针与slow指针相遇,必定在环中相遇。

  1. 寻找环的入口

在这里插入图片描述
我们要求的是x,slow指针走过的节点数x+y
fast指针走过的节点数x+y+n(y+z) n为fast指针在环内走了n圈才遇到slow指针,(y+z)为一圈节点的个数
同时,fast指针走过的节点数= 2*slow指针走过的节点数(x+y)*2 = x+y+n(y+z)
两边消掉一个(x+y):x+y=n(y+z),因为要求的是x, x=n(y+z)-y,再将这个式子变形,得到x=(n-1)(y+z)+z
重点分析此等式,因为n是fast转的圈数。一定大于等于1,当n等于1的时候,得到等式x=z。不管n等于多少,相遇点都在环入口位置。
所以,计算环的开始位置,就是计算z,可以记录上次快慢指针的相交位置,当快慢指针相遇的时候,让其中一个回到头结点,然后同速前进,再次相遇即为环开始的位置


代码:

  • 24.两两交换链表中的节点
//迭代法
var swapPairs = function(head) {
    if(!head || !head.next) return head
    const dummyHead = new ListNode(0,head)
    let pre = dummyHead
    while(pre.next && pre.next.next) {
        let node1 = pre.next
        let node2 = pre.next.next

        pre.next = node2
        node1.next = node2.next
        node2.next = node1
        pre = node1
    }

    return dummyHead.next
};
//递归法
var swapPairs = function(head) {
    if(!head || !head.next) return head
    const newHead = head.next

    head.next = swapPairs(head.next.next)
    newHead.next = head 

    return newHead
};
  • 19 . 删除链表的倒数第N个节点
var removeNthFromEnd = function(head, n) {
    const dummyHead = new ListNode(0, head)
    let slow = fast = dummyHead
    while(n--) {
        fast = fast.next
    }
    //快指针再多走一步
    fast = fast.next
    while(fast){
        slow = slow.next
        fast = fast.next
    }
    //删除节点的操作
    slow.next = slow.next.next
    return dummyHead.next
};
  • 面试题02.07 链表相交
var getIntersectionNode = function(headA, headB) {
    let curA = headA
    let curB = headB
    let lenA = getListLen(headA)
    let lenB = getListLen(headB)

    if(lenA < lenB) {
        //注意这里需要加分号!
        [curA, curB] = [curB, curA];
        [lenA, lenB] = [lenB, lenA]
    }

    let i = lenA - lenB
    while(i--) {
        curA = curA.next
    }
    //注意这里是判断节点地址是否相同,不是节点的值
    while(curA && curA !== curB) {
        curA = curA.next
        curB = curB.next
    }

    return curA
};

var getListLen = function(head) {
    let len = 0
    let cur = head
    while(cur) {
        len++
        cur = cur.next
    }
    return len
}
  • 142 . 环形链表Ⅱ
var detectCycle = function(head) {
    //首先判断链表是否有环
    if(!head || !head.next) return null
    let slow = head.next
    let fast = head.next.next
    while(fast && fast.next && fast !== slow) {
        slow = slow.next
        fast = fast.next.next
    }
    if(!fast || !fast.next) return null

    //让slow指针从头开始,两个指针同速前进
    slow = head
    while(fast !== slow) {
        slow = slow.next
        fast = fast.next
    }
    return slow
};

总结:

真的好困啊啊啊啊啊明天继续

删除链表节点的关键是,操作要删除的链表的节点的上一个位置。删除倒数第n个节点,可以利用双指针的方式巧妙控制位置。
环形链表两个问题,一,判断是否有环,当快指针遇到了空,当然没有环。二,判断链表环的位置,利用数学公式推导,可以发现快慢指针相交位置到环入口处的距离,等于链表开始位置到环入口处的距离,所以可以在两快慢指针相交后,把慢指针放回头部,两节点同速移动,到下次相遇的位置就是环的入口位置。

这些方法要是我自己想真的很难想到,不得不说有些解题思路真是太妙了!!!终于追平进度啦~链表结束,哈希表我来咯

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值