链表中倒数最后k个节点
输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
数据范围:0≤n≤105,0≤ai>≤109>,0≤k≤109
要求:空间复杂度 O(n),时间复杂度 O(n)
进阶:空间复杂度 O(1),时间复杂度 O(n)
例如输入{1,2,3,4,5},2时,对应的链表结构如下图所示:
其中蓝色部分为该链表的最后2个结点,所以返回倒数第2个结点(也即结点值为4的结点)即可,系统会打印后面所有的节点来比较。
示例1
输入:
{1,2,3,4,5},2
返回值:
{4,5}
说明:
返回倒数第2个节点4,系统会打印后面所有的节点来比较。
示例2
输入:
{2},8
返回值:
{}
解法1:两次遍历
(1)第一次遍历得到链表的长度
(2)第二次遍历返回倒数k个节点
public ListNode FindKthToTail (ListNode pHead, int k) {
// write code here
int len = 0;
int i = 0;
ListNode newNode = pHead;
while(pHead != null)
{
len++;
pHead = pHead.next;
}
if(len<k || k == 0) return null;
while(newNode != null)
{
if(i==(len - k)) return newNode;
i++;
newNode = newNode.next;
}
return null;
}
解法2:双指针
可以使用双指针的方法来优化这个算法,避免两次遍历链表。具体步骤如下:
-
定义两个指针
first
和second
,都初始指向链表头pHead
。 -
让
first
指针先向前移动k
步。 -
然后同时移动
first
和second
指针,直到first
指针到达链表末尾。 -
此时
second
指针指向的就是倒数第k
个节点。
因为 second
指针要 移动 length
(链表长度)- k 步,而 fist
移动到第 k 个节点后, second
和 fist
同时移动, 当 fist
移动到链表末尾,走了 length - k
步,即 second
也从头走了 length - k
步,即为所求节点。
public ListNode FindKthToTail (ListNode pHead, int k) {
// write code here
ListNode first = pHead;
ListNode second = pHead;
// first 移动 k 次
for(int i = 0; i < k; i++)
{
if(first == null) return null;
first = first.next;
}
// 两个指针同时移动
while(first != null)
{
first = first.next;
second = second.next;
}
return second;
}