1.题目
2.思路
2.1 普通方法:遍历两次链表--O(n)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
int cnt = 0;
ListNode tail = head;
while(tail != null){
cnt++;
tail = tail.next;
}
int index = cnt - n;
ListNode ans = new ListNode(-1);
ans.next = head;
ListNode tail2 = ans;
// System.out.println(cnt+">>>");
int temp = 0;
while(tail2 != null){
if(temp == index){
tail2.next = tail2.next.next;
tail2 = tail2.next;
}
else{
tail2 = tail2.next;
}
temp++;
}
return ans.next;
}
}
2.2 双指针:快慢指针--O(n)
使用快慢指针思路。
快指针先走n,慢指针再跟上。那么快指针到达链表末尾时,慢指针正好指在倒数第n个节点。
实际操作中,由于删除节点需要上一个节点的地址,因此我们设置一个dummy(哑巴节点、哨兵),慢指针指在要删除节点的上一个节点上。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// // 1.扫描两次
// int cnt = 0;
// ListNode tail = head;
// while(tail != null){
// cnt++;
// tail = tail.next;
// }
// int index = cnt - n;
// ListNode ans = new ListNode(-1);
// ans.next = head;
// ListNode tail2 = ans;
// // System.out.println(cnt+">>>");
// int temp = 0;
// while(tail2 != null){
// if(temp == index){
// tail2.next = tail2.next.next;
// tail2 = tail2.next;
// }
// else{
// tail2 = tail2.next;
// }
// temp++;
// }
// return ans.next;
// 2.扫描一次的算法--双指针!!!快慢指针
ListNode fast = new ListNode(-1);
fast.next = head;
ListNode slow = fast;
ListNode ans = slow;
int cnt = 0;
int flag = 0;
while(slow != null){
cnt++;
if(fast != null)
fast = fast.next;
if(flag == 0 && fast == null){
slow.next = slow.next.next;
flag = 1;
}
if(cnt > n){
slow = slow.next;
}
}
// System.out.println(fast +">" + slow);
return ans.next;
}
}
3.结果