题目描述
链表结点定义如下:
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
思路分析
为了得到倒数第k个结点,很自然的想法是先走到链表的尾端,再从尾端回溯k步,可是本题中的单链表的结点没有从后往前的指针,所以这种思路行不通。
假设整个链表有n个结点,那么倒数第k个结点就是从头结点开始的第n-k+1个结点,如果我们可以得到链表中结点的个数n,那么我们只要从头结点往后走n-k+1步就可以了。得到结点个数仍需要遍历一遍链表。
但是上述方法需要遍历链表两次,但是往往要求我们只能遍历一次链表,此时如何实现呢?
为了实现只遍历一次链表,我们可以定义两个指针:第一个指针从链表的头指针开始遍历向前走k-1,第二个指针保持不动;从第k步开始,第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离始终保持在k-1,当第一个指针(走在前面的)到达链表尾结点时,第二个指针刚好指向倒数第k个结点。
关于如何想到这个方法,可以逆向思考:倒数第k个结点与尾结点之间正好差k-1个结点,所以只要从最开始,让两个结点相差k-1个结点,然后在此基础上“同步前进”,当前面的指针到达尾结点时,后面的结点即到达指定位置。
注意
为保证代码的鲁棒性,有三个特殊情况需要注意:
1.链表为空链表。2.k为0。 3.链表的结点总数大于n。
Java实现
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
//特殊值处理,很重要!
//考虑输入为空链表或者k为0,都为特殊情况,返回null
if(head==null||k==0)
return null;
//先走的指针
ListNode p1=head;
//后走的指针
ListNode p2=null;
for(int i=1;i<=k-1;i++){
//判断当输入的k是否大于实际链表中元素的个数
if(p1.next!=null)
p1=p1.next;
//如果在循环内部,p.next==null,则证明此时k是大于实际链表的元素个数的,返回空
else
return null;
}
//p1和p2之间始终相差k-1个元素,当p1指向尾结点时,p2指向的结点刚好为要查找的结点
p2=head;
while(p1.next!=null){
p1=p1.next;
p2=p2.next;
}
return p2;
}
}