[LeetCode] 19. Remove Nth Node From End of List

32 篇文章 0 订阅
19 篇文章 0 订阅

原题链接: https://leetcode.com/problems/remove-nth-node-from-end-of-list/

1. 题目介绍

Given a linked list, remove the n-th node from the end of list and return its head.
给定一个链表,删除倒数第 n 个元素,然后返回新链表的头指针

Example:

Given linked list: 1->2->3->4->5, and n = 2.

After removing the second node from the end, the linked list becomes 1->2->3->5.

Note:
Given n will always be valid.
Follow up:
Could you do this in one pass?

注意:保证给定的 n 是有效的

可以在一遍循环中实现吗?

2. 解题思路

这个题非常适合作为链表类题目的入门题。要求做的事情,非常简单,只需要删除链表中倒数第 n 个元素即可。

我们来看一下节点中的属性和函数:

public class ListNode {
     int val;
     ListNode next;
     ListNode(int x) { val = x; }
}

我们可以知道,这是一个单向链表,一个节点只能直到自己后面的节点是谁,而不知道前面的节点是谁。


注: 以下内容参考了 https://leetcode.com/problems/remove-nth-node-from-end-of-list/solution/

2.1 思路1:遍历两次

由于不能知道前面的节点是谁,我们可以有这样的思路:

  1. 求出整个链表中有多少个元素。比如有length个元素
  2. 然后对第length - n +1个元素进行移除,移除的方法就是让第 length-n 个节点的 next 直接指向 length -n +2 个节点。

总共遍历2次,一次是计算总长度length,一次是删除节点。
在遍历链表的过程中,有个技巧需要注意:

技巧:使用辅助节点

我们将添加一个无用的结点作为辅助节点,这个节点放在链表的最前面,算是第 0 个节点吧。辅助节点用来简化某些极端情况,例如链表中只含有一个结点,或需要删除的节点就是链表的头节点等等。

实现代码

/**
 * 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) {
		
		//辅助节点,用于简化链表节点只有1个、删除头节点 等情况
		//放在整个链表的最前面
		ListNode assist = new ListNode(0);
		assist.next = head;
		
		//求链表的长度:第一次遍历链表
		int length = 0;
		ListNode temp = head;
		while(temp != null) {
			length ++;
			temp = temp.next;
		}
		
		int index = length - n;
        //删除链表某个节点:第二次遍历
		temp = assist;
        while(index >0) {
			index --;
			temp = temp.next;
		}
		//此时temp会指向要删除的节点的前一个节点
		//然后删除该节点即可
		temp.next = temp.next.next;
		
		return assist.next;
		//这里不能return head,原因是head节点可能也是被删除的节点
    }
}

2.2 思路2:一次遍历

上面的方法,为什么会遍历两次?因为一次求总长度,一次要删数组。

怎样才能做到只遍历一次呢?删除数组的那次遍历是省略不了的,那就只能省略求总长度的那次了。
于是问题转化为:如何在不知道总长度的情况下,删除链表的倒数第 n 个数。

下面有请 “双指针法” 隆重登场。

技巧:双指针法

定义一个左指针 left , 定义一个右指针 right 。左指针指向第 0 个元素,右指针指向第 n+1 个元素。让左指针和右指针之间,始终隔着 n 个元素,就像一个固定宽度的窗口一样。然后将这个窗口向右平移。

当右指针到达最后一个节点时,左指针就会指向链表的倒数第 n+1 个元素了。于是我们把倒数第n+1 个元素的 next 指针绕过 要删除的倒数第 n 个元素,指向倒数第 n-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) {
		
		//辅助节点,用于简化链表节点只有1个、删除头节点 等情况
		//放在整个链表的最前面
		ListNode assist = new ListNode(0);
		assist.next = head;
		
		ListNode left = assist;
		ListNode right = assist;
		
		//让right指向第n+1个元素,让左指针和右指针之间,始终保持 n 个元素的距离
		for(int i = 1 ; i<=n+1 ; i++) {
			right = right.next;
		}
		//平移窗口
		while(right != null) {
			right = right.next;
			left = left.next;
		}
		
		left.next = left.next.next;
		return assist.next;
		//这里不能return head,原因是head节点可能也是被删除的节点
    }
}

3. 参考资料

https://leetcode.com/problems/remove-nth-node-from-end-of-list/solution/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值