链表中倒数最后k个结点
描述
输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
例如输入{1,2,3,4,5},2时,对应的链表结构如下图所示:
其中蓝色部分为该链表的最后2个结点,所以返回倒数第2个结点(也即结点值为4的结点)即可,系统会打印后面所有的节点来比较。
示例1
输入:{1,2,3,4,5},2
返回值:{4,5}
说明:返回倒数第2个节点4,系统会打印后面所有的节点来比较。
示例2
输入:{2},8
返回值:{}
解法一:反转链表实现
思路: 先反转链表为newHead,倒数第k个变为第k个,然后再用头插法插入newHead中的前k个创建一个新链表kHead,输出。
如图:
public ListNode FindKthToTail (ListNode pHead, int k) {
// 一、先反转链表
// 1、创建新链表
ListNode newHead=null;
// 2、接受当前节点
ListNode cur=pHead;
// n用于接受链表长度
int n=0;
// 3、遍历旧链表
while(cur!=null){
// 4、临时保存下一节点
ListNode temp=cur.next;
// 5、当前节点,指向新链表头结点
cur.next=newHead;
// 6、更新新链表头结点
newHead=cur;
// 7、下一节点
cur=temp;
n++;
}
// 二、再次反转链表,输出第k个
// 1、创建新链表
ListNode kHead=null;
// 2、接受反转后的链表
ListNode p=newHead;
// count用于和k比较
int count=1;
// 3、判断链表长度是否小于k
if(n<k){
return null;
}
// 4、输入倒数k个结点,后面代码与反转链表时相同,不同的是遍历条件
while(count<=k){
ListNode temp2=p.next;
p.next=kHead;
kHead=p;
p=temp2;
count++;
}
return kHead;
}
复杂度分析:
- 时间复杂度:O(N)
- 空间复杂度: O(1)
解法二:快慢指针
- 第一个指针先移动k步,然后第二个指针再从头开始,这个时候这两个指针同时移动,当第一个指针到链表的末尾的时候,返回第二个指针即可
public ListNode FindKthToTail (ListNode pHead, int k) {
// 1、定义快慢指针
ListNode fast=pHead;
ListNode slow=pHead;
// 2、快指针先走k步
int count=1;
while(count<=k){
// 如果fast=null说明k大于链表长度
if(fast==null){
return null;
}
count++;
fast= fast.next;
}
// 3、双指针同时走
while(fast!=null){
fast=fast.next;
slow=slow.next;
}
// 4、返回慢指针
return slow;
}
复杂度分析:
- 时间复杂度:O(N)
- 空间复杂度: O(1)
解法三:栈实现
- 把原链表的结点全部压栈,然后出栈返回第k个结点
public ListNode FindKthToTail (ListNode pHead, int k) {
// 判空
if (pHead == null || k == 0){
return null;
}
// 1、创建栈
Stack<ListNode> stack = new Stack<>();
// 2、链表节点压栈
while (pHead != null) {
stack.push(pHead);
pHead = pHead.next;
}
// 3、判断栈的元素是否小于k
if (stack.size() < k){
return null;
}
// 4、定义返回结点为栈顶结点
ListNode newHead = stack.pop();
while (--k > 0) {
// 5、返回出栈后的第k个结点
newHead =stack.pop();
}
return newHead;
}
复杂度分析:
- 时间复杂度:O(N)
- 空间复杂度: O(N)