三算法求得 链表中倒数第k个节点

182 阅读2分钟

下雨天冷,大家注意保暖

正题

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

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

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

这是一道相对简单的算法题,求倒数第K个节点的问题。首先想到的就是将每一次链表的遍历存储在一个数组里面,然后取得倒数第K个元素即可,所以有了方法一。这也是最容易想到,最简单的解题思路,也只需要遍历一次即可。优势是只需要遍历一次,缺点是需要开辟新的数组空间,对内存有一点消耗。

方法一:数组缓存法

ScreenRecorderProject15.gif

代码:

var getKthFromEnd = function(head, k) {
    let list = []
    while(head) {
        list.push(head)
        head = head.next
    }

    return list[list.length-k]
};

方法二: 二次遍历法

既然方法一种提到了优势是只遍历一次,那么说明我们还可以遍历多次(实际上是2次即可)取得正确结果。其实也很好理解,第一次遍历记录下链表的长度,然后第二次遍历只遍历到 长度 - k 个节点即可。优缺点和方法一互补。

ScreenRecorderProject16.gif

代码:

var getKthFromEnd = function(head, k) {
    let index = 0
    let p = head
    while(p) {
        index++
        p = p.next
    }
    while(head) {
        if (k === index--) {
            return head
        }
        head = head.next
    }
}

方法三: 双指针遍历法

双指针遍历法是二次遍历法的延伸,我们可以定义两个指针去遍历链表,区别在于他们的出发时机不同,P1指针优先出发,P2指针在P1指针出发K个节点之后开始出发,划重点:那么我们可以得知P1和P2之间相隔了K个节点,那么当P1指针遍历结束时,P2指针指向的就是倒数第K个节点了!

ScreenRecorderProject18.gif

代码:

var getKthFromEnd = function(head, k) {
    let fast = head
    let slow = head
    while(fast) {
        if (k <=0) {
            slow = slow.next
        }
        fast = fast.next
        k--
    }
    return slow
}