leetcode题目解析(js)--链表

206. 反转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

解法:

var reverseList = function(head) {
    let cur = head, prev = null
    while(cur) {
        [cur.next, prev, cur] = [prev, cur, cur.next]
    }
    return prev
};
复制代码

思路分析:prev保存前一个节点, 主要逻辑分三步:

  1. 修改cur的next,指向前一个节点,即prev
  2. 保存当前节点cur为prev,供下一个节点使用
  3. 移动当前节点指针cur到下一个节点cur.next

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

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例:

给定 1->2->3->4, 你应该返回 2->1->4->3.

解法一:

var swapPairs = function(head) {
    let node = new ListNode(0) //新建一个节点,用于保存head节点
    node.next = head
    let prev = node
    while(prev.next && prev.next.next) {
        let a = prev.next
        let b = a.next
        prev.next = a.next
        a.next = b.next
        prev = b.next = a
    }
    return node.next
};
复制代码

解题思路: 定义一个新的头节点node,将头节点next指向head,定义prev, a, b分别指向前三项,第一遍循环如下图:

循环过程中node始终指向自定义的头结点,而node.next即为真正的头节点。
遍历第二遍时,a, b分别指向3,4,执行流程同上,执行完第二遍即结束循环。

解法二:

var swapPairs = function(head) {
    if(!head) return null
    if(!head.next) return head
    let temp = head.next
    head.next = swapPairs(temp.next)
    temp.next = head
    return temp
};
复制代码

解题思路:主要逻辑是head.next指向递归返回的上一个temp,同时修改temp.next为head,实现后一个节点指向前一个节点,同时前一个节点指向递归返回的节点,最后swapPairs接收到的就是最后返回的temp,即第二个节点2.

141. 环形链表

给定一个链表,判断链表中是否有环。

示例:

输入:head = [3,2,0,-4], 输出:true

解法一:使用快慢指针,快指针每次走两步,慢指针每次走一步,如果有环,二者必定会碰到一起;

var hasCycle = function(head) {
    let fast = slow = head
    while(fast && slow && fast.next) { //fast.next不存在说明链表已走完
        slow = slow.next
        fast = fast.next.next
        if(fast === slow) {
            return true
        }
    }
    return false
};
复制代码

解法二:使用Set保存每个节点的地址,如果有环,Set里元素必定会重复;

var hasCycle = function(head) {
    let set = new Set()
    while(head) {
        if(set.has(head)) return true
        set.add(head)
        head = head.next
    }
    return false
};
复制代码

142. 环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

示例:

输入:head = [3,2,0,-4]
输出:tail connects to node index 1

解法一:

var detectCycle = function(head) {
    let set = new Set()
    let cur = head
    while(cur) {
        if(set.has(cur)) return cur
        set.add(cur)
        cur = cur.next
    }
    return null
};
复制代码

解题思路:用Set保存每次遍历的地址,遍历时发现地址重复即为入口节点

解法二:

var detectCycle = function(head) {
    let fast = slow = head
    while(slow && fast && fast.next) {
        slow = slow.next
        fast = fast.next.next
        if(slow === fast) {
            slow = head
            while(slow !== fast) {
                slow = slow.next
                fast = fast.next
            }
            return fast
        }
    }
    return null
};
复制代码

解题思路:

  1. 先用快慢指针方法确定链表是否有环
  2. 如果有环的,慢指针slow重新从head出发,slow,fast每次都走一步,如果重合,重合处必为环的入口;

入口解释一下:

首先第一次遍历后假设在C点相遇,此时:

慢指针走的S1 = |AB| + |BC|
快指针走的S2 = 2 * ( |AB| + |BC| )

在另一个角度,S2 走的距离就是 |AB| + |BC| + n * s (s为环的大小),即

快指针走的S2 = |AB| + |BC| + n * s

所以可以得到

2 * ( |AB| + |BC| ) = |AB| + |BC| + n * s
可以得到:|AB| = n * s - |BC|

可以看出如果slow从A点开始,fast从相遇点C开始,两者以同样速度必会在B点相遇

你可能感兴趣的:(leetcode题目解析(js)--链表)