1. 实现一个算法,找出单向链表中倒数第k个节点(这是CC150的一道题目)
2. 从题目可以看出,只是给出了链表的头结点,所以我们是不知道链表中含有几个元素,假如知道几个元素的话我们就比较好处理了,可以使用一个计数变量来记录我们需要遍历多少次我们才可以找到倒数第k个节点,但是麻烦就在这里,只知道头结点,这里我们使用非常有技巧性的做法就是使用两个指针(双型指针)来进行移动,指针分别是p1,p2,分别指向的是头结点(这里的头结点是哑元)让p1,p2一开始的时候距离相隔为k - 1
一开始的时候指针p2先移动,先移动k个长度,使指针p1,p2保持k - 1个距离,然后p1,p2同时移动,直到p2移动到链表的末尾,此时p1指向的元素就是我们需要求解的倒数第k个节点,这里使用到的双型指针的思路是非常巧妙和经典的
可以在纸上结合具体的例子来判断程序的逻辑是否正确
例子:1,2,3,4,5,求出倒数第2个元素
一开始的时候p1指向的是哑元(空节点)p2先进行移动指向的是第二个元素
p1与p2同时进行移动,while循环中的p2的值一次指向的是2,3,4,5,p1在while循环里面指向的1,2,3,4,所以最后返回的是p1指针指向的元素对应的值
p1与p2同时移动的时候两者指针相隔的距离为k - 1
3. 具体的代码如下:
public class Main{
//使用的是双型指针,先第二个指针先移动k个长度的距离使一开始的时候两个指针保持一段距离
private static class ListNode{
private ListNode next;
private Object value;
public ListNode(Object value) {
super();
this.value = value;
}
}
public static void main(String[] args) {
int arr[] = {1, 2, 3, 4, 5};
//int arr[] = {1, 2, 3, 4, 5, 8, 10, 12, 67};
//哑元
//这里使不使用哑元构建第一个元素都是可以的
ListNode head = new ListNode(null);
ListNode p = head;
for(int i = 0; i < arr.length; i++){
//使用数组来生成链表
p.next = new ListNode(arr[i]);
p = p.next;
}
int k = 2;
removeNode(head, k);
}
private static void removeNode(ListNode head, int k){
//特别要注意边界的问题,传入的k可能是一个非法的值
if(k <= 0){
return;
}
ListNode p1 = head;
ListNode p2 = head;
int count = 0;
//指针p2先移动,使指针p1与指针p2相隔的距离为k - 1
while(count < k){
p2 = p2.next;
count++;
}
System.out.println(p2.value);
while(p2 != null){
p1 = p1.next;
p2 = p2.next;
}
System.out.println(p1.value);
}
}
这里是否用哑元来构建头结点都是可以的,不构建哑元那么头结点就是一个元素对应的节点,此外这里还需要注意的是边界的问题,有可能传入的k是一个非法的值所以我们在调用方法之前需要进行判断