题目描述
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
类似的题目还有返回倒数第k个节点的值
解题思路
两趟扫描
- 分两趟扫描链表
- 第一趟用于获取链表长度size,然后算出第二趟应该扫描的节点个数size-k。
- 第二趟从头开始遍历size-k个节点。得到的即为倒数第k个节点
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode cur = head;
int count = 0;
// 获取链表节点个数
while (cur != null) {
count++;
cur = cur.next;
}
// 遍历得到倒数第k个节点
for (int i = 0; i < count - k ; i++) {
head = head.next;
}
return head;
}
快慢指针 一趟扫描
- 使用两个指针向后扫描,
- 一个快指针多走k步,一个慢指针指向头节点
- 两个节点同时向后走,当快指针到头时,慢指针指向的即为倒数第k个节点。
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
// 快慢指针先都指向head
ListNode fast = head;
ListNode slow = head;
// 快指针多走k步
while (k-- != 0) {
fast = fast.next;
}
// 两个指针同步向后 直到快指针为空 此时慢指针即为倒数第k个节点
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
栈
- 先遍历链表,把所有节点入栈,此时出栈顺序即为链表倒序
- 出栈k-1个节点,此时栈顶即为倒数第k个节点
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
Stack<ListNode> stack = new Stack<>();
// 所有节点都入栈
while (head != null) {
stack.push(head);
head = head.next;
}
// 出栈k-1个节点,此时栈顶即为倒数第k个节点
k--;
while (k-- != 0) {
stack.pop();
}
return stack.peek();
}
}
递归
弄懂递归的原理,递归分为两个过程,递和归,看一下下面的图就知道了,先往下传递,当遇到终止条件的时候开始往回走。
这题如果使用递归的话,我们先来看一下递归的模板
public ListNode getKthFromEnd(ListNode head, int k) {
//终止条件
if (head == null)
return head;
//递归调用
ListNode node = getKthFromEnd(head.next, k);
//逻辑处理
……
}
终止条件很明显就是当节点head为空的时候,就没法递归了,这里主要看的是逻辑处理部分,当递归往下传递到最底端的时候,就会触底反弹往回走,在往回走的过程中记录下走过的节点,当达到k的时候,说明到达的那个节点就是倒数第k个节点,直接返回即可,如果没有达到k,就返回空,搞懂了上面的过程,代码就很容易写了。
//全局变量,记录递归往回走的时候访问的结点数量
int size;
public ListNode getKthFromEnd(ListNode head, int k) {
//边界条件判断
if (head == null)
return head;
ListNode node = getKthFromEnd(head.next, k);
++size;
//从后面数结点数小于k,返回空
if (size < k) {
return null;
} else if (size == k) {
//从后面数访问结点等于k,直接返回传递的结点k即可
return head;
} else {
//从后面数访问的结点大于k,说明我们已经找到了,
//直接返回node即可
return node;
}
}
上面代码在仔细一看,当size小于k的时候node节点就是空,所以我们可以把size大于k和小于k合并为一个,这样代码会更简洁一些
int size;
public ListNode getKthFromEnd(ListNode head, int k) {
if (head == null)
return head;
ListNode node = getKthFromEnd(head.next, k);
if (++size == k)
return head;
return node;
}