19. 删除链表的倒数第 N 个结点
题目:
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
1
2
示例 2:
输入:head = [1], n = 1
输出:[]
1
2
示例 3:
输入:head = [1,2], n = 1
输出:[1]
1
2
提示:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
进阶:你能尝试使用一趟扫描实现吗?
解法1:暴力
/**
* 思路:
* 获取链表长度
* n==len返回head.next
* 遍历链表,到倒数第n-1个时候进行删除操作
* 返回head
*/
public ListNode removeNthFromEnd(ListNode head, int n) {
if (n==0){
return head;
}
ListNode curr = head;
int len=0;
while (curr!=null){
curr=curr.next;
len++;
}
if (n==len){
return head.next;
}
curr=head;
for (int i=0;i<len;i++){
if (i==len-n-1){
curr.next=curr.next.next;
break;
}
curr=curr.next;
}
return head;
}
时间复杂度:On
空间复杂度:O1
解法2:双指针
/**
* 思路:
* 设置一个前驱节点指向head
* 前驱节点赋值给慢指针,head赋值给快指针
* 快指针走n步
* 之后快慢指针一起移动,直到快指针为null
* 删除节点
* 返回新的头结点pre.next
*/
public static ListNode removeNthFromEnd(ListNode head, int n) {
if(n==0){
return head;
}
ListNode pre = new ListNode(-1);
ListNode fast = head;
ListNode slow = pre;
slow.next=head;
while (n!=0){
fast=fast.next;
n--;
}
while (fast!=null){
fast=fast.next;
slow=slow.next;
}
slow.next = slow.next.next;
return pre.next;
}
时间复杂度:On
空间复杂度:O1
解法3:hash
/**
* 思路:
* 创建map存放节点的位置和节点
* 遍历节点:确定节点数量,map中放入数据
* 通过map获得要删除的节点
* 如果节点的头节点,返回head.next
* 获取要删除节点的前一个节点
* 删除节点
*/
public static ListNode removeNthFromEnd(ListNode head, int n) {
if (head==null){
return head;
}
ListNode curr = head;
HashMap<Integer,ListNode> map = new HashMap<>();
int count =0;
while (curr!=null){
map.put(count,curr);
curr=curr.next;
count++;
}
ListNode delNode = map.get(count - n);
if (delNode!=null) {
if (delNode == head) {
return head.next;
}
ListNode preNode = map.get(count - n - 1);
preNode.next = delNode.next;
}
return head;
}
时间复杂度:Onlogn
空间复杂度:On
解法4:集合
/**
* 和map的思路,写法一样,只不过这里换成了ArrayList集合
*/
public static ListNode removeNthFromEnd(ListNode head, int n) {
ArrayList<ListNode> list = new ArrayList<>();
ListNode curr = head;
while (curr!=null){
list.add(curr);
curr=curr.next;
}
int size = list.size();
if (size - n!=size) {
ListNode delNode = list.get(size - n);
if (delNode == head) {
return head.next;
}
ListNode preNode = list.get(size - n - 1);
preNode.next = delNode.next;
}
return head;
}
时间复杂度:On
空间复杂度:On
解法5:反转链表
/**
* 思路:
* 如果n==0没有要删除的直接返回
* 反转链表
* 删除倒数第n个节点
* 如果n==1,删除反转后的第一个节点,走到下个节点就好
* 如果n!=1,需要找到第n个节点的前一个节点进行删除操作
* 在反转回来
*/
public static ListNode removeNthFromEnd(ListNode head, int n) {
if (n==0){
return head;
}
head=reverse(head);
ListNode curr = head;
int i=0;
if (n==1){
head=head.next;
}else {
while (i!=n-2){
curr=curr.next;
i++;
}
curr.next=curr.next.next;
}
head=reverse(head);
return head;
}
private static ListNode reverse(ListNode head) {
if (head==null){
return head;
}
ListNode curr = head;
ListNode next = curr.next;
while (next!=null){
ListNode nn = next.next;
next.next=curr;
head.next=nn;
curr=next;
next=nn;
}
return curr;
}
时间复杂度:On
空间复杂度:On
解法6:递归
/**
* 思路:
* 设置全局变量level=0倒着记录链表的位置
* 确定链表长度
* 如果n==i,直接返回head.next
* 否则进入递归
* 递归到尾节点
* 如果当前层是n,进行删除操作
*/
static int level=0;
public static ListNode removeNthFromEnd(ListNode head, int n) {
ListNode curr = head;
int i=0;
while (curr!=null){
curr=curr.next;
i++;
}
if (n!=i) {
return recursive(head, n);
}else {
return head.next;
}
}
private static ListNode recursive(ListNode head, int n) {
if (head==null||head.next==null){
level=1;
return head;
}
ListNode tail=recursive(head.next, n);
if (level==n){
head.next=tail.next;
}
level++;
return head;
}
时间复杂度:On
空间复杂度:On