「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战」
前言
笔者除了大学时期选修过《算法设计与分析》和《数据结构》还是浑浑噩噩度过的(当时觉得和编程没多大关系),其他时间对算法接触也比较少,但是随着开发时间变长对一些底层代码/处理机制有所接触越发觉得算法的重要性,所以决定开始系统的学习(主要是刷力扣上的题目)和整理,也希望还没开始学习的人尽早开始。
系列文章收录《算法》专栏中。
问题描述
给你一个链表,删除链表的倒数第 n **个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
提示:
- 链表中结点的数目为 sz
- 1 <= sz <= 30
- 0 <= Node.val <= 100
- 1 <= n <= sz
进阶: 你能尝试使用一趟扫描实现吗?
确定学习目标
对《删除链表的倒数第 N 个结点》的算法过程进行剖析。
剖析
我们先确认下两个要主要功能:
- 快速定位要删除的第N个结点。
- 删除节点
删除节点没啥好说的,链表节点移除而已。
那么怎么快速定位要删除的节点呢?我们很清楚:链表删除和插入的效率比较高,但是随机访问效率比较低,那我们是不是可以直接遍历一遍塞进list呢?当然可以,其实我也想过遍历一遍效率会比较低但是,数据结构已经给了,你要重新构造其他数据结构必须遍历一遍。所以我们可以使用list就行了。
还有一个问题,给定方法的参数直接是带关键字的头节点,如果要删除中间或者最后的节点那还好,如果删除头节点的话就没头节点了,所以我们直接再设置一个头节点nodeHead,这样我们就能正常对链表进行删除,结果直接返回nodeHead.next就行了。
还有一个需要注意的就是,没有pre指针,我们需要使用list去访问要删除的头节点的前面一个节点,需要注意数组越界。
下面直接上代码。
代码
import java.util.ArrayList;
import java.util.List;
/**
* 19. 删除链表的倒数第 N 个结点
* https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
*
* 我们先确认下两个要主要功能:
* 1. 快速定位要删除的第N个结点。
* 2. 删除节点
*
* 删除节点没啥好说的,链表节点移除而已。
*
* 那么怎么快速定位要删除的节点呢?我们很清楚:链表删除和插入的效率比较高,但是随机访问效率比较低,那我们是不是可以直接遍历一遍塞进list呢?当然可以,其实我也想过遍历一遍效率会比较低但是,数据结构已经给了,你要重新构造其他数据结构必须遍历一遍。所以我们可以使用list就行了。
*
* 还有一个问题,给定方法的参数直接是带关键字的头节点,如果要删除中间或者最后的节点那还好,如果删除头节点的话就没头节点了,所以我们直接再设置一个头节点nodeHead,这样我们就能正常最链表进行删除,结果直接返回nodeHead.next就行了。
*/
public class RemoveNthFromEnd {
/**
* 头节点
*/
private ListNode nodeHead = new ListNode();
/**
* 存放节点的list,用于快速定位要删除的节点
*/
private List<ListNode> nodeList = new ArrayList<>();
/**
* 节点个数
*/
private int nodeCount = 0;
/**
* 节点结构
*/
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;
}
}
/**
* 移除倒数第n个节点
*
* @param head
* @param n
* @return
*/
public ListNode removeNthFromEnd(ListNode head, int n) {
//使我们新增加的头节点字段和方法传参头节点连接上
nodeHead.next = head;
/**
* 准备遍历节点装进list里面,add的同时nodeCount++
*/
ListNode forNode = head;
while (forNode.next != null) {
nodeList.add(forNode);
nodeCount++;
forNode = forNode.next;
}
nodeList.add(forNode);
nodeCount++;
//定位出要删除的节点
ListNode delNode = nodeList.get(nodeCount - n);
//获得要删除节点前面一个节点的index方便后面删除节点
int delNodeBeforeIndex = nodeCount - n - 1;
//需要注意删除的节点前面一个节点的index是否数组越界了,如果越界了直接用头节点
ListNode delNodeBeforeNode;
if (delNodeBeforeIndex < 0) {
delNodeBeforeNode = nodeHead;
} else {
delNodeBeforeNode = nodeList.get(delNodeBeforeIndex);
}
//删除节点的下一个节点方便后面删除节点
ListNode delNodeNextNode = delNode.next;
delNodeBeforeNode.next = delNodeNextNode;
return nodeHead.next;
}
}