解法
- 第一种思路是从前向后遍历到链表尾,再从尾部回溯k-1次。由于是单链表所以这种方法不能实现
- 第二种思路是如果已知链表长度为n那么倒数k个节点为n-k+1个节点。链表长度需要遍历一遍链表才能知道,这种解法一共需要遍历两次
- 有没有只遍历一次的解法呢?这时使用两个指针即可解决问题。
- 使用一个指针A指向链表头部,向后移动k-1次。
- A指针移动K-1次后,使用指针B指向头节点,此时两个指针之间相差k-1步
- 同时移动A、B指针,当A指针到达链表尾部时,B指针所指节点即为倒数第k个节点
/**
* 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 pAhead = head;
ListNode pBhead = null;
for(int i = 0; i < k-1; i++)
pAhead = pAhead.next;
pBhead = head;
while(pAhead.next != null){
pBhead = pBhead.next;
pAhead = pAhead.next;
}
return pBhead;
}
}
鲁棒性
这题需要注意代码的鲁棒性,上述代码还存在如下问题:
- 输入的head为空引用时怎么处理
- 如果链表长度小于k,在for循环中指针会向前走k-1步,会照成控制异常
- 在c/c++中如果,k类型为unsigned int 时,如果k 为 0,在for循环中k-1为4294967295。循环的执行次数远远超出估计
鲁棒的代码如下:
/**
* 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) {
if(head == null || k <= 0) return null;
ListNode pAhead = head;
ListNode pBhead = null;
for(int i = 0; i < k-1; i++){
if(pAhead.next != null){
pAhead = pAhead.next;
}else {
return null;
}
}
pBhead = head;
while(pAhead.next != null){
pBhead = pBhead.next;
pAhead = pAhead.next;
}
return pBhead;
}
}