[路飞]_前端算法第二十弹-剑指 Offer 22. 链表中倒数第k个节点

128 阅读2分钟

「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

示例:

给定一个链表:1->2->3->4->5, 和k= 2.

返回链表 4->5.

顺序遍历

看到这道题我们第一想法就是按顺序查找,我们先计算出链表head的长度,然后再确定倒数第K个节点在正序遍历中是第几个节点,返回该节点即可。

var getKthFromEnd = function(head, k) {
    let node = head, n = 0;
    // 计算链表的长度
    while (node) {
        node = node.next;
        n++;
    }
    node = head;
    // 遍历到倒数第K个节点为止
    for (let i = 0; i < n - k; i++) {
        node = node.next;
    }
    return node; 
};

复杂度分析

  • 时间复杂度:O(n),其中 n 为链表的长度。需要两次遍历。
  • 空间复杂度:O(1)。

双指针法

正序遍历这道题并不难,而我们需要通过这道题锻炼我们对于双指针的敏感度,敏感度对于算法计算十分重要,我们需要通过大量的练习达到当我们看一道这种关于链表节点的问题,就应该第一时间想到是否应该使用双指针来进行计算。

这道题我们就可以使用双指针进行计算,首先我们先将head赋值给dummy用于遍历,我们再将head分别遍历给fast快指针和slow慢指针,快指针先行,当走到正数K节点时停止,此时dummy链表的长度就应为head链表的长度-K。此时当dummy继续遍历时,对slow链表进行遍历,当dummy遍历到最后一个节点时,此时的slow便遍历到了我们所需的倒数第K个节点。

var getKthFromEnd = function (head, k) {
    // 将用于遍历的dummy,和快fast慢slow指针都指向head
  let dummy = head;
  let fast = head;
  let slow = head;
    // 节点计数器
  let count = 1;
	// 遍历终止条件
  while (dummy.next != null) {
    // count < k 时,快节点遍历到正数K的位置
    if (count < k) {
      fast = fast.next
			count++
    } else {
    // 慢节点遍历到倒数K节点的位置
      slow = slow.next
    }
    dummy = dummy.next
  }
  return slow
};

复杂度分析

  • 时间复杂度:O(n),其中 n 为链表的长度。我们使用快慢指针,只需要一次遍历即可,复杂度为 O(n)。
  • 空间复杂度:O(1)