学算法刷LeetCode【剑指offer专题】:22. 链表中倒数第k个节点

282 阅读2分钟

题目描述

image.png

解题思路

思路一: 倒数变正数

我们可以将倒数第k个节点变成正数第 n - k 个节点。

单链表算倒数第 k 个节点的难处在于无法从尾向头遍历,只能从头向尾遍历,但是如果我们知道链表的长度 n,知道要返回倒数第 k 个节点,那么转换成计算:链表的正数第 h 个节点,这样不就好做了嘛。

那我们怎么将倒数第k个节点变成正数第 h 个节点呢?直接将链表的长度 - k即可,即 h = n - k

image.png

接下来我们只需要再遍历一次链表,然后让链表在数到 h 的时候返回即可。

再梳理一遍:

  • 计算出链表长度 n
  • 计算出正数位置 h: h = n - k
  • 遍历单链表,并在 h 处时返回节点。

代码

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} k
 * @return {ListNode}
 */
var getKthFromEnd = function(head, k) {
    if(!head) return head;

    // 计算单链表长度 n
    let n = 0;
    for(let cur = head; cur != null; cur = cur.next){
        n++;
    }

    // 计算正数为第 h 个节点
    let h = n - k;
    
    // 遍历将正数第 h 个元素返回
    let cur= head;
    while(cur || h){
        if(h === 0){
            return cur;
        }
        cur = cur.next;
        h--;
    }
};

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

思路二: 快慢指针

也是遍历,让快指针先走 k 步,慢指针再走,等快指针走完整个链表,慢指针所在位置即为倒数 第 k 个节点的位置。

实际上是思路一的变形。

代码

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} k
 * @return {ListNode}
 */
var getKthFromEnd = function(head, k) {
    if(!head) return head;

    let slow = head, fast = head;
    for(let i = 0; i < k; i++){
        fast = fast.next;
    }

    while(fast){
        fast = fast.next;
        slow = slow.next;
    }

    return slow;
};

时间复杂度和空间复杂度同上。

总结

只要想清楚正数是第 n - k 步即可。 不管是先算出链表的整个长度,再计算正数是第 n - k 节点,还是用快慢指针,让快指针先走 k 步,然后快慢指针一起走,最后慢指针就是倒数第 k 个节点, 目的都是一样的。