一、题目
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、代码实现
(1)两次遍历
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0); //设置哑结点
dummy.next = head;
int length = 0;
ListNode first = head;
while(first != null){ //第一次遍历找出列表长度
length++;
first = first.next;
}
length -= n; //删除链表倒数第n个结点,也就是正数第L-n+1个结点
first = dummy;
while(length > 0){
length--;
first = first.next;
}
first.next = first.next.next; //删除结点
return dummy.next; //返回头结点
}
}
执行结果:
时间复杂度:O(L)
空间复杂度:O(1)
(2)一次遍历(双指针法)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0); //设置哑结点
dummy.next = head;
ListNode first = dummy;
ListNode second = dummy;
for(int i = 1;i <= n+1;i++){
first = first.next; //第一个指针先向前移动n+1步
}
while(first != null){ //第一个指针到达最后一个结点
first = first.next;
second = second.next;
}
second.next = second.next.next; //删除结点
return dummy.next;
}
}
执行结果:
时间复杂度:O(L)
空间复杂度:O(1)
三、收获
在两次遍历算法中,可以把删除链表倒数第n个结点转化成删除链表正数第L-n+1个结点,重点在于求出链表的长度L,为此我们需要进行一次遍历;在一次遍历算法中,灵活的运用了双指针,让第一个指针先移动n+1步之后,再同时移动第一个指针和第二个指针,直至第一个指针到达链表的尾结点。