题目难度中等,考查的是链表基础
解题涉及到的数据结构:链表,栈。
需要注意的点:可能删除的节点是第一个节点,所以需要一个哑元节点来进行删除。
方法一: 遍历链表,得出链表的长度,然后把定位到需要删除的节点,删除,就OK啦。
// 解法一,遍历一次链表得到链表的长度,然后就可以执行删除操作了,链表的题最主要的就是要理解哑元节点这个概念,
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode listNode = head;
// 需要注意,需要一个临时节点来代替head节点,否则删除头结点的时候删除不了
ListNode result = new ListNode(-1, head);
ListNode dummy = result;
int len = 0;
while (listNode != null){
len++;
listNode = listNode.next;
}
for (int i = 0; i < len - n; i++){
dummy = dummy.next;
}
dummy.next = dummy.next == null ? null : dummy.next.next;
return result.next;
}
方法二:使用栈。(栈的特性,后进先出)
// 解法二,使用栈的方式, 需要记住栈的数据结构,先进后出
public ListNode removeNthFromEnd2(ListNode head, int n) {
ListNode dummy = new ListNode(-1, head);
ListNode node = dummy;
Deque<ListNode> stack = new LinkedList<>();
while (node != null){
stack.push(node);
node = node.next;
}
for (int i = 0; i < n; i++){
stack.pop();
}
ListNode pop = stack.pop();
pop.next = pop.next == null ? null : pop.next.next;
return dummy.next;
}
方法三: 快慢指针。
// 解法三,使用双指针,快慢指针
// 无论是遍历链表再删除,还是使用栈的方式,其实都是要先遍历一次链表,其实可以遍历一次链表就做到定位到需要删除的节点
public ListNode removeNthFromEnd3(ListNode head, int n) {
ListNode dummy = new ListNode(-1, head);
ListNode fast = dummy; // 快指针
ListNode slow = dummy; // 慢指针
for (int i = 0; i < n + 1; i++){
fast = fast.next;
}
while (fast != null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next == null ? null : slow.next.next;
return dummy.next;
}
接下来是完整的代码和测试用例。
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
/**
* @author xnl
* @Description:
* @date: 2022/5/15 16:22
*/
public class Solution {
// 测试方法
public static void main(String[] args) {
Solution solution = new Solution();
ListNode l1 = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));
// ListNode l2 = new ListNode(1);
ListNode l2 = new ListNode(1, new ListNode(2));
solution.removeNthFromEnd3(l2, 2).print();
}
// 解法一,遍历一次链表得到链表的长度,然后就可以执行删除操作了,链表的题最主要的就是要理解哑元节点这个概念,
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode listNode = head;
// 需要注意,需要一个临时节点来代替head节点,否则删除头结点的时候删除不了
ListNode result = new ListNode(-1, head);
ListNode dummy = result;
int len = 0;
while (listNode != null){
len++;
listNode = listNode.next;
}
for (int i = 0; i < len - n; i++){
dummy = dummy.next;
}
dummy.next = dummy.next == null ? null : dummy.next.next;
return result.next;
}
// 解法二,使用栈的方式, 需要记住栈的数据结构,先进后出
public ListNode removeNthFromEnd2(ListNode head, int n) {
ListNode dummy = new ListNode(-1, head);
ListNode node = dummy;
Deque<ListNode> stack = new LinkedList<>();
while (node != null){
stack.push(node);
node = node.next;
}
for (int i = 0; i < n; i++){
stack.pop();
}
ListNode pop = stack.pop();
pop.next = pop.next == null ? null : pop.next.next;
return dummy.next;
}
// 解法三,使用双指针,快慢指针
// 无论是遍历链表再删除,还是使用栈的方式,其实都是要先遍历一次链表,其实可以遍历一次链表就做到定位到需要删除的节点
public ListNode removeNthFromEnd3(ListNode head, int n) {
ListNode dummy = new ListNode(-1, head);
ListNode fast = dummy; // 快指针
ListNode slow = dummy; // 慢指针
for (int i = 0; i < n + 1; i++){
fast = fast.next;
}
while (fast != null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next == null ? null : slow.next.next;
return dummy.next;
}
}
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
public void print(){
System.out.print(this.val + " ");
if (this.next != null){
this.next.print();
}
}
}