2020.3.23
力扣刷题日常,原题:点击此处
考点:链表遍历,快慢指针
题解:
1.这道题是876题的升级版本,读者可以先做876题热身,也可以做完这题回去巩固快慢指针的知识。
2.删除链表的倒数第 n 个节点,很明显,我们肯定想要的是一次遍历的方法,而不是二次遍历。
3.思路一(我个人的思路):参照876题解法,快指针走n格,慢指针走n-1格,到最后快指针已经不能在走的时候,从慢指针的位置向后面一个一个的检查,哪个是倒数第n个节点。
这个方法存在表现很差的时候,比如:
假设一条很长很长的链表,要删除倒数第二个元素,那么到最后快指针肯定比慢指针多走了一倍,而慢指针要从链表中间向后检查,时间复杂度可能甚至超越二次遍历。因此,这道题的快慢指针不能用876题的做法。
4.思路二:打从一开始,快指针就设计在n+1的位置。
这样做,就可以保证每次快慢指针中间都夹着n个元素。
当快指针到null时,就是倒数第n个元素。
为了保证快指针一开始就可以设计在n+1的位置,设置哑结点(dummy)在头结点之前。
下面为代码:
/**
* 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 fast = dummy;
ListNode slow = dummy;
//到达n+1的位置
for(int i = 0; i <= n;i++){
fast = fast.next;
}
while(fast != null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
}
}
思路一的代码:
可以AC,我觉得时间复杂度以及到达O(n2)了,不推荐学习。
/**
* 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) {
// System.out.println(head.val);用于测试头结点
ListNode slow = head;
ListNode fast = head;
boolean flag = false;
while(true){
ListNode temp = fast;
for(int i = 0; i <n ; i++){
temp = temp.next;
// 如果已经超出范围了,则退出
if(temp == null){
flag = true;
break;
}
}
if(flag){ break;}
// 慢指针记录上一个快指针的位置
slow = fast;
// 快指针更新
fast = temp;
}
// 如果快和慢指向同一个,说明删除头结点
if(slow == fast){
head = head.next;
return head;
}
while(slow != null){
ListNode temp = slow;
for(int i = 0; i < n; i++){
temp = temp.next;
}
if(temp.next == null){
slow.next = slow.next.next;
break;
}
slow = slow.next;
}
return head;
}
}