今是昨非

今是昨非

日出江花红胜火,春来江水绿如蓝

鏈結串列的雙指針算法

链表的双指针算法#

876. 链表的中间结点#

给定一个单链表的头节点 head,返回链表的中间节点。

如果有两个中间节点,则返回第二个中间节点。

示例 1:

输入: head = [1,2,3,4,5]
输出: [3,4,5]
解释: 链表的中间节点是节点 3。

示例 2:

输入: head = [1,2,3,4,5,6]
输出: [4,5,6]
解释: 链表的中间节点是节点 4 和 5,返回第二个中间节点。

解法#

根据例子,看起来是获取链表中间位置到末尾的节点,这与双指针有什么关系呢?看到给出的代码,明白了,不是数组,给出的是一个 ListNode。

定义的代码如下:


/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     public var val: Int
 *     public var next: ListNode?
 *     public init() { self.val = 0; self.next = nil; }
 *     public init(_ val: Int) { self.val = val; self.next = nil; }
 *     public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
 * }
 */
class Solution {
    func middleNode(_ head: ListNode?) -> ListNode? {

    }
}

一开始没理解给出的 ListNode 和上面的示例中的 head 是如何对应起来的。

经过一段时间的思考,我明白了,

链表
1 -> 2 -> 3 -> 4 -> 5

val     1
next    2

val     2
next    3

val     3
next    4

val     4
next    5

val     5
next    nil

理解了 ListNode 之后,那如何获取中间的 ListNode 呢?

定义两个变量,s 和 f,每次循环 s 指向下一个元素,而 f 指向下下个元素,这样最后 f 结束时,s 指向的就是中间。


/*
最开始
f
1 -> 2 -> 3 -> 4 -> 5
s

第一次循环
		  f
1 -> 2 -> 3 -> 4 -> 5
     s
	 
第二次循环
		            f
1 -> 2 -> 3 -> 4 -> 5
          s

第三次循环
当 f 的 next 为空时结束,此时 s 指向的是中间

f = fast pointer
s = slow pointer
*/

最终代码如下:


/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     public var val: Int
 *     public var next: ListNode?
 *     public init() { self.val = 0; self.next = nil; }
 *     public init(_ val: Int) { self.val = val; self.next = nil; }
 *     public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
 * }
 */
class Solution {
    func middleNode(_ head: ListNode?) -> ListNode? {
        var slow = head
        var fast = head
        while (fast != nil) && (fast?.next != nil) {
            slow = slow?.next
            fast = fast?.next?.next
        }
        return slow
    }
}

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

给定一个链表的头节点 head,删除链表的倒数第 n 个节点,并返回链表的头节点。

示例 1:

输入: head = [1,2,3,4,5], n = 2
输出: [1,2,3,5]

示例 2:

输入: head = [1], n = 1
输出: []

示例 3:

输入: head = [1,2], n = 1
输出: [1]

解法#

这个地方,需要注意的是 "倒数第 n 个结点",是从末尾数第几个。

我的想法是首先获取整个链表的长度,然后整个长度减去 (n - 1),就是正着数的第几个节点,从 1 开始计数,然后赋值,从头开始赋值,如果刚好到这个位置,则跳过。
但是使用双指针的算法是,定义 slow 和 fast,然后先让 fast 向前走 n 步,再让 slow 和 fast 一起向前走,这样当 fast 走到尾部时,slow 刚好指向要移除的节点,然后跳过这个节点赋值。

代码如下:


/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     public var val: Int
 *     public var next: ListNode?
 *     public init() { self.val = 0; self.next = nil; }
 *     public init(_ val: Int) { self.val = val; self.next = nil; }
 *     public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
 * }
 */
class Solution {
    func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {
        let node = ListNode(0, head)
        
        var slow: ListNode? = node
        var fast: ListNode? = node
        
        var m = n
        while m > 0 {
            fast = fast?.next
            m -= 1
        }
        
        while fast?.next != nil {
            slow = slow?.next
            fast = fast?.next
        }
        
        slow?.next = slow?.next?.next
        
        return node.next
    }
}

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。