剑指 Offer(第2版)面试题 22:链表中倒数第 k 个节点
剑指 Offer(第2版)面试题 22:链表中倒数第 k 个节点
题目来源:33. 链表中倒数第 k 个节点
解法1:先求链表长度再遍历
两次遍历链表:
- 第一次遍历求得链表的长度。
- 第二次遍历求得链表中倒数第 k 个节点的位置。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution
{
public:
ListNode *findKthToTail(ListNode *pListHead, int k)
{
if (pListHead == nullptr || k == 0)
return nullptr;
int len = getListLength(pListHead);
if (k > len)
return nullptr;
int step = len - k;
ListNode *p = pListHead;
while (step--)
p = p->next;
return p;
}
// 辅函数 - 得到链表的长度
int getListLength(ListNode *pListHead)
{
int listLength = 0;
while (pListHead)
{
listLength++;
pListHead = pListHead->next;
}
return listLength;
}
};
复杂度分析:
时间复杂度:O(n),其中 n 是链表的长度。
空间复杂度:O(1)。
解法2:双指针
两次遍历不够优秀,我们希望一次遍历搞定。
定义两个指针 fast 和 slow,让 fast 先走 k 步,再让 fast 和 slow 同时向后遍历,当 fast 到达链表末尾的空指针时,slow 指向链表中倒数第 k 个节点。
注意 k 可以比链表长度还大。在程序中,当 fast 走到空指针时,k 仍然大于 0,说明最开始的 k 大于链表长度,直接返回 nullptr 即可。
代码:
class Solution
{
public:
ListNode *findKthToTail(ListNode *pListHead, int k)
{
if (pListHead == nullptr || k == 0)
return nullptr;
ListNode *fast = pListHead, *slow = pListHead;
while (k--)
{
fast = fast->next;
if (fast == nullptr)
break;
}
if (fast == nullptr && k)
return nullptr;
while (fast)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
复杂度分析:
时间复杂度:O(n),无论什么情况,都要遍历完一次链表。
空间复杂度:O(1),只用到了 2 个指针的额外空间。