单链表倒数第k个节点问题
问题描述
输入一个链表,输出该链表中倒数第k个节点。注意链表结点计数从1开始,即链表的尾结点是倒数第1个结点。例如一个链表有6个结点,从头开始它们的值依次为1、2、3、4、5、6。这个链表的倒数第3个结点是值为4的结点。
问题分析
由于是单链表所以,要想找到倒数第k个结点,首先得知道链表长度,进行一次遍历得到链表的长度length,然后用length-k+1即得到需要遍历输出的结点位置进行第二次遍历,输出第length-k+1个结点的值。但是如此一来就要进行两次遍历了,时间复杂度上太高;如何在一次遍历的情况下输出结果呢?
解题思路
双指针法:定义两个指针,一个快,一个慢;快的指针比慢的指针快k个结点;然后两个指针同时开始遍历,当快的指针指向尾结点时,慢的指针将指向倒数第k个结点。但是在此过程中需要注意判断输入的k是否合法(不能为0;不能超过链表的长度)
举一反三:
1、求链表的中间结点。如果链表中结点总数为奇数,返回中间结点;如果结点总数是偶数,返回中间两个结点的任意一个。为了解决这个问题,我们也可以定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步。当走得快的指针走到链表的末尾时,走得慢的指针正好在链表的中间。
2、判断一个单链表是否形成了环形结构。定义两个指针,同时从链表的头结点出发,一个指针一次走一步,另一个指针一次走两步。如果走得快的指针追上了走得慢的指针,那么链表就是环形链表;如果走得快的指针走到了链表的末尾(.next==null)都没有追上第一个指针,那么链表就不是环形链表。
public class Code015 {
public static void main(String[] args){
ListNode node1=new ListNode(1);
ListNode node2=new ListNode(2);
ListNode node3=new ListNode(3);
ListNode node4=new ListNode(4);
ListNode node5=new ListNode(5);
ListNode node6=new ListNode(6);
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=node5;
node5.next=node6;
ListNode pListHead=node1;
Scanner sc=new Scanner(System.in);
int k=sc.nextInt();
ListNode resultNode=FindKthToTail(pListHead,k);
System.out.println(resultNode.value);
}
private static ListNode FindKthToTail(ListNode pListHead, int k) {
//判断k是否合法,以及单链表是否为空
if(pListHead==null || k<=0){
return null;
}
ListNode pFast=pListHead;
ListNode pSlow=null;
//因为是从1开始计数;因此快指针应该是比慢指针快k-1个结点
for(int i=0;i<k-1;i++){
if(pFast.next!=null){
pFast=pFast.next;
}else {//如果k超过链表长度,那么将返回null
return null;
}
}
//定义慢指针初始指向头结点
pSlow=pListHead;
//开始遍历,遍历结束条件为快指针指向链表的尾(即快指针的下一个结点指向null)
while (pFast.next!=null){
pFast=pFast.next;
pSlow=pSlow.next;
}
return pSlow;
}
private static class ListNode{
int value;
ListNode next;
public ListNode() {
}
public ListNode(int value) {
this.value = value;
}
}
}