一.题目
输入一个链表,输出该链表中倒数第k个结点。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
//
}
}
二.分析:
- 由题目给出的
ListNode
类可知,该链表是一个单链表,具有单向可达性 ,所以“先找到最后一位,然后在回溯到第k位”这一思路是行不通的; - 假设链表长度为
n
,那么倒数第k
项就是顺数第n-k+1
项;马上形成第二种思路“首先遍历一遍求得n
,在遍历一遍求得n-k+1
项”; ok,这思路没问题.但是如果只能遍历一遍的话,那么这个解法就不满足了. - 整两个指针(初始化指向第一个Node),一个指针(rightNode)先走
k-1
步,然后两指针一同往后走,直至第一个指针走到最后一个Node处,此时第二个指针(leftNode)指向的Node就是倒数第k个Node;
三.进一步分析
上诉思路是很清晰的,但是这个题目需要考察的使我们代码的鲁棒性,而容错性其重要的一个体现;我们思考的需要很缜密,对于题目中给出的方法的参数head,k
要进行严格判断.若如下三种情况出现但是没有合理的处理,程序会崩溃:
1. k<=0
,此时的k是没有意义的,应为”倒数第k位”,只有当k>0时,才有意义;
2. 当head=null
时,此时链表就不存在,也是没有意义的.
3. 当k>大于链表结点数
时,也是没有意义的.
四.代码
分析完之后,代码就简单了,直接贴:
public ListNode FindKthToTail(ListNode head,int k) {
//正常情况下
ListNode rightNode=null;
ListNode leftNode=null;
if(head!=null&&k>0){//处理第1,2中情况
rightNode=head;
leftNode=head;
}else{
return null;
}
for(int i=0;i<k-1;i++){
if(rightNode.next!=null){ //处理第3种情况
rightNode=rightNode.next;
}else{
return null ;
}
}
while(rightNode.next!=null){
leftNode=leftNode.next;
rightNode=rightNode.next;
}
return leftNode;
}
五.扩展
扩展1:判断一个单链表是否形成环形的结构.
思路:定义两个指针,同时从链表的头结点出发,一个指针一次都两步,一个指针一次走一步;如果走得快的指针追上了走得慢的指针,那么该单链表存在环;如果做得快的指针走到了next=null处,那么该单链表就不存在环;
扩展2:求链表的中间结点;但节点数为奇数时,返回中间结点,当结点数为偶数时,返回中间两个结点的任意一个节点;
思路:整两指针,一个一次走两步,一个一次走一步,当走得快的指针走到末尾时,走得慢的指针就放好指向链表的中间结点;
六.总结
当我们用一个指针不能解决链表问题的时 候,我们可以用两个指针,一个走得快一点,一个走的慢一点,或者可以让一个指针在链表中先走上若干步;同时也要注意代码的鲁棒性,对输入进行严格的检查;