牛客网AC地址:http://www.nowcoder.com/books/coding-interviews/529d3ae5a407492994ad2a246518148a?rp=1
《剑指offer》面试题15:链表中倒数第k个结点
题目:输入一个链表,输出该链表中倒数第k个结点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第一个结点。如一个链表有6个结点,从头结点开始它们的值依次是1、2、3、4、5、6,这个链表的倒数第3个结点是值为4的结点。
链表的结点定义如下:
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
思路:由单链表的结构可知,单向链表的结点只有从前往后的指针而没有从后往前的指针。
如果可以遍历链表两次,我们可以第一次遍历链表的时候得到结点的个数n;然后再从头结点开始往后走n-k+1步就可以了。
**但面试官期待的解法只需要遍历链表一次呢?
我们可以定义两个指针。第一个指针从链表的头指针开始遍历向前走k-1,第二个指针保持不动;
从第k步开始,第二个指针也开始从链表的头指针开始遍历。
由于两个指针的距离保持在k-1,当第一个(走在前面的)指针到达链表的尾节点时,第二个指针(走在后面的)指针正好是倒数第k个结点。
下面是一个例子:在有6个结点的链表上找倒数第3个节点
根据上述思路可写出代码:
public class Solution {
public ListNode FindKthToTail(ListNode head, int k) {
ListNode pre = null;
ListNode behind = null;
if (head == null || k <= 0) { // 如果链表为空,或k不满足条件
return null;
}
pre = head;
for (int i = 0; i < k - 1; i++) { // 第一个指针向前走k-1步
if (pre.next != null) {
pre = pre.next;
} else { // 结点数目小于k
return null;
}
}
behind = head;
while (pre.next != null) { // 两个指针同时向后走
pre = pre.next;
behind = behind.next;
}
return behind;
}
}
这里需要注意几个边界条件:
1) 链表为空
2) k没有意义,比如为0,甚至是负数
3) 链表的长度不足k
起初一直运行错误,就是因为这几个边界条件考虑得不足,下面是自己第一次AC的代码(思路大致一样,只是定义了很多变量,用于统计两个指针的步数等等):
public class Solution {
public ListNode FindKthToTail(ListNode head, int k) {
ListNode kthToTail = null;
ListNode pre = null;
ListNode behind = null;
if (head == null || k <= 0) {
return null;
}
pre = behind = head;
int temp = 0; // 用于计数
int length = 0; // 统计链表的长度,对k的最大值进行判断
while (pre != null) {
if (pre.next == null) { // 前一个结点到了最后,后一个结点刚好处在倒数k个位置
kthToTail = behind;
if (k - 1 > length) {
return null;
}
break;
}
if (temp == (k - 1)) { // 前一个指针遍历了(k-1)步,同时移动这两个指针
pre = pre.next;
behind = behind.next;
length++;
} else {
pre = pre.next; // 前一个指针先先后遍历
temp++;
length++;
}
}
return kthToTail;
}
}
最后是测试代码(这里可以多想一些测试用例,多测试几次):
public class Main {
public static void main(String[] args) {
ListNode list = null;
ListNode node_1 = new ListNode(1);
ListNode node_2 = new ListNode(3);
ListNode node_3 = new ListNode(5);
list = node_1;
list.next = node_2;
list.next.next = node_3;
Solution tstSolution = new Solution();
ListNode kth = tstSolution.FindKthToTail2(list, 2);
if (kth!=null) {
System.out.println(kth.val);
}
}
}