初一看这个似乎并不像我们前面学习到的「快慢指针法」的考察。所以大多数人就迷糊了,进入到常规化思考。依然还是设置一个整型变量 count,然后每次循环的时候 count++,拿到链表的长度 n。那么倒数第 k 个节点也就是顺数第 n-k+1 个结点。所以我们只需要在拿到长度 n 后再进行一次 n-k+1 次循环就可以拿到这个倒数第 k 个节点的值了。
但面试官显然不会太满意这个臃肿的解法,他依然希望我们一次循环就能搞定这个事。
为了实现只遍历一次链表就能找到倒数第 k 个结点,我们依然可以定义两个遍历 slow 和 fast。我们让 fast 变量先往前遍历 k-1 步,slow 保持不动。从第 k 步开始,slow 变量也跟着 fast 变量从链表的头结点开始遍历。由于两个变量指向的结点距离始终保持在 k-1,那么当 fast 变量到达链表的尾结点的时候,slow 变量指向的结点正好是我们所需要的倒数第 k 个结点。
我们依然可以在心中默认一遍代码:
假设输入的链表是:1->2->3->4->5;
现在我们要求倒数第三个结点的值,即顺数第 3 个结点,它的值为 3;
定义两个变量 slow、fast,它们均指向结点 1;
先让 fast 向前走 k-1 即 2 步,这时候 fast 指向了第 3 个结点,它的值是 3;
现在 fast 和 slow 同步向右移动;
fast 再经过了 2 步到达了链表尾结点;fast 正好指向了第 3 个结点,这显然是符合我们的猜想的。
在心中默走了一遍代码后,我们显然很容易写出下面的代码。
public class Test15 { public static class LinkNode { int data; LinkNode next; public LinkNode(int data) { this.data = data; } } private static int getSpecifiedNodeReverse(LinkNode head, int k) { LinkNode slow = head; LinkNode fast = head; if (fast == null) { throw new RuntimeException("your linkNode is null"); } // 先让 fast 先走 k-1 步 for (int i = 0; i < k - 1; i++) { if (fast.next == null) { // 说明输入的 k 已经超过了链表长度,直接报错 throw new RuntimeException("the value k is too large."); } fast = fast.next; } while (fast.next != null) { slow = slow.next; fast = fast.next; } return slow.data; } public static void main(String[] args) { LinkNode head = new LinkNode(1); head.next = new LinkNode(2); head.next.next = new LinkNode(3); head.next.next.next = new LinkNode(4); head.next.next.next.next = new LinkNode(5); System.out.println(getSpecifiedNodeReverse(head, 3)); System.out.println(getSpecifiedNodeReverse(null, 1)); } } 总结