题意描述:
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
解题思路:
Alice: 常规的做法是遍历两遍链表,第一遍统计节点个数,然后计算从正面数是第几个节点,然后第二遍遍历输出。
Bob: 也可以使用数组先把所有节点都存下来,然后直接返回,只遍历一遍链表就可以了,不过费内存。
Alice: 还可以考虑双指针,只要两个指针保持 K
个间距,当后面的指针到达链表末尾的 null
,前面的指针就指向了倒数第 K
个节点。
Bob: 我怎么就想不到呢。😢😢
Alice: 不过这道题倒是没有什么边界值之类的测试点。
代码:
Python 方法一:两次遍历
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
cnt = 0
node = head
while node != None:
node = node.next
cnt += 1
bound = cnt - k
node = head
for x in range(bound):
node = node.next
return node
Python 方法二:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
tmp = []
node = head
while node != None:
tmp.extend([node])
node = node.next
index = len(tmp) - k
return tmp[index]
Python 方法三: 双指针,保持 K 个距离的双指针。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
"""
node 和 head 为两个保持了k个间距的指针,当node到达None,head 指向倒数第 K 个节点
"""
node = head
for x in range(k):
node = node.next
while node != None:
node = node.next
head = head.next
return head
Java 方法一: 统计链表元素个数 + 两次遍历
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
int cnt = 0;
ListNode node = head;
while(node != null){ // 统计链表中元素的总个数
cnt += 1;
node = node.next;
}
k = cnt - k + 1; // 计算需要返回的元素的索引
node = head;
for(int i=1; i<k; ++i){ // 返回
node = node.next;
}
return node;
}
}
Java 方法二: 使用 ArrayList
保存节点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ArrayList<ListNode> tmp = new ArrayList<ListNode>();
ListNode node = head;
while(node != null){
tmp.add(node);
node = node.next;
}
int index = tmp.size() - k;
return tmp.get(index);
}
}
Java 方法三: 双指针。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode node = head;
for(int i=0; i<k; ++i){
node = node.next;
}
while(node != null){
node = node.next;
head = head.next;
}
return head;
}
}
易错点:
- 一些测试用例:
[1,2,3,4,5]
2
[1,2,3,4,5]
5
[1,2]
2
[1]
1
- 答案:
[4,5]
[1,2,3,4,5]
[1,2]
[1]
总结:
- 双指针真是常用啊。