LeetCode:返回倒数第k个结点
1、题目描述
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。
注意:本题相对原题稍作改动
示例:
输入: 1->2->3->4->5 和 k = 2
输出: 4
说明:
给定的 k 保证是有效的。
2、方法1:两次遍历
-  
第一次遍历:计算链表的长度
len。 -  
第二次遍历:从头节点开始,移动
len - k步,到达倒数第k个节点。 
关键步骤
-  
计算链表长度:
-  
初始化指针
curr指向头节点head,计数器len = 0。 -  
遍历链表,每移动一次
curr,len加 1,直到curr为null。 -  
最终
len存储的是链表的节点总数。 
 -  
 -  
定位倒数第 k 个节点:
-  
倒数第
k个节点就是正数第len - k + 1个节点。 -  
由于链表从
head开始编号为1,所以只需移动len - k步即可到达目标节点。 -  
例如,链表
1->2->3->4->5,k=2(倒数第 2 个节点是4):-  
len = 5,len - k = 3。 -  
从
head移动 3 步:1(初始)→2→3→4,返回4。 
 -  
 
 -  
 
时间复杂度
-  
O(n),其中
n是链表的长度。-  
第一次遍历计算长度:
O(n)。 -  
第二次遍历定位节点:
O(n)。 -  
总时间:
O(2n) = O(n)。 
 -  
 
空间复杂度
-  
O(1),只使用了常数级别的额外空间(
curr指针和len变量)。 
public static ListNode kthToLast(ListNode head, int k) {ListNode curr = head;int len = 0;// 第一次遍历:计算链表长度 lenwhile (curr != null) {len++;curr = curr.next;}// 第二次遍历:移动 len - k 步for (int i = 0; i < len - k; i++) {head = head.next;}return head;  // 返回倒数第 k 个节点
} 
3、方法2:快慢指针
使用快慢指针(Fast-Slow Pointer)来找到链表的倒数第 k 个节点:
-  
初始化快慢指针:
fast和slow都指向头节点head。 -  
快指针先移动
k-1步:-  
目的是让
fast和slow之间相隔k-1个节点。 -  
这样当
fast到达链表末尾时,slow正好指向倒数第k个节点。 
 -  
 -  
同步移动快慢指针:
-  
同时移动
fast和slow,每次各移动一步,直到fast到达最后一个节点(fast.next == null)。 
 -  
 -  
返回慢指针:
-  
此时
slow指向的就是倒数第k个节点。 
 -  
 
关键点
-  
快指针先移动
k-1步:-  
确保
fast和slow之间的间隔是k-1。 -  
例如,
k=2时,fast先移动 1 步,指向第 2 个节点,slow仍在第 1 个节点,两者间隔 1(即k-1)。 
 -  
 -  
同步移动的条件:
-  
while (fast != null && fast.next != null):-  
fast != null:避免fast已经越界(理论上不会发生,因为题目保证k有效)。 -  
fast.next != null:确保fast可以移动到下一个节点(即fast不是最后一个节点)。 
 -  
 -  
当
fast是最后一个节点时(fast.next == null),循环停止,此时slow指向倒数第k个节点。 
 -  
 
时间复杂度
-  
O(n),其中
n是链表的长度。-  
快指针最多移动
n步(先移动k-1步,再同步移动n-k步)。 -  
慢指针移动
n-k步。 -  
总时间:
O(n)。 
 -  
 
空间复杂度
-  
O(1),只使用了两个指针(
fast和slow),没有额外空间消耗。 
public static ListNode kthToLast2(ListNode head, int k) {ListNode fast = head, slow = head;// 快指针先移动 k-1 步for (int i = 0; i < k - 1; i++) {fast = fast.next;}// 同步移动快慢指针,直到快指针到达最后一个节点while (fast != null && fast.next != null) {fast = fast.next;slow = slow.next;}return slow;  // 慢指针指向倒数第 k 个节点
} 
