给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
题目来源:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
方案一: 暴力解决,由于于删除链表的倒数第n个节点,那么就是删除链表的顺数第(length - n + 1)个节点,length表示链表的节点个数。那么这时候我们需要定义一个方法,将这个链表的节点个数进行统计从而获取这个链表的长度,然后通过从头结点开始循环遍历,找到要删除的节点(顺数第length - n + 1)的节点,之后将这个节点的前一个节点接在这个节点的后一个节点的前面,从而实现删除链表的倒数第n个数。
对应的代码:
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
int count = length(head);
if(head == null || n > count)//如果链表为空,或者n比链表的长度大,那么就不需要进行删除了,直接返回头结点
return head;
ListNode current = head,pre = null;
int i = 1;
while(current != null && i < (count - n + 1)){
pre = current;
current = current.next;
i++;
}
if(pre == null){
head = current.next;//如果删除的是头结点,那么删除之后的链表头结点是原来链表头节点的下一个节点
}else{
//删除的不是头结点,那么就使这个删除节点的前一个节点接在删除节点的下一个节点前面
pre.next = current.next;
}
return head;
}
/*
调用这个方法,从而获取链表的长度
*/
public int length(ListNode head){
int count = 0;
ListNode current = head;
while(current != null){
count++;
current = current.next;
}
return count;
}
}
方法二:由于删除倒数第n个节点,那么我们利用栈先进后出的特性,只要将链表的所有节点通过遍历压入栈中,压栈完毕之后,只要跳出n个节点,那么这时候栈顶的节点就是删除节点的前一个节点,这时候只需要将这个栈顶结点接在删除节点的下一个节点即可实现删除链表的倒数第n个节点。
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode root = new ListNode(0,head);//创建一个带有假节点的链表
Stack<ListNode> stack = new Stack<ListNode>();
/*
这一步非常重要,为什么需要将假节点一起压入到栈中呢?
因为如果删除的是头结点,那么如果没有将假节点压入到栈中,
那么操作就会相对麻烦些
*/
ListNode current = root;
while(current != null){
stack.push(current);
current = current.next;
}
for(int i = 0; i < n; i++){
//跳出n个节点,从而进行删除倒数第n个节点
stack.pop();
}
//删除倒数n个节点之后,那么这时候栈顶节点就是删除节点的前一个节点,将它赋值给pre这个节点
ListNode pre = stack.peek();
pre.next = pre.next.next;//通过这一步,从而删除了倒数第n个节点
/*
注意这里不应该返回head,而应该返回root.next,为什么呢?如果要删除的头结点,
那么就会导致返回的链表依旧含有头结点,但是如果返回的是root.next,此时得到的是删除节点之后的链表
*/
return root.next;
}
方法三:利用双指针进行解决问题
思路:
1、定义两个指针变量,pre和current,其中pre表示删除节点的前一个节点,那么这时候只要进行pre.next = pre.next.next就可以实现删除节点了
2、要实现上面的删除操作,只要pre移动了length - n步,那么这时候就可以找到删除节点的前一个节点了,但是由于我们并没有定义一个方法获取链表的长度,所以我们利用了current这个指针,首先current这个指针位于头结点,然后让current走n步,此时current位于第n + 1个节点的位置,这时候还有len - n - 1个节点没有走,而pre依旧处于假节点的位置,之后两者都进行遍历,直到current为null(必须是current为null的时候才可以结束后移,这时候才满足了pre移动了length - n步,否则,如果是遍历到链表的最后一个节点,那么实际上pre才移动到length - n - 1步,显然不满足),pre表示删除节点的前一个节点
。
3、pre.next = pre.next.next
,从而删除倒数第n个节点。
对应的代码:
class Solution {
//利用双指针进行,此时pre表示删除节点的前一个节点
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode root = new ListNode(0,head);//创建一个带有假节点的链表
ListNode pre = root;
ListNode current = root.next;
int i;
//通过这一步,从而将current移动到第n + 1个节点
for(i = 1; i <= n; i++)
current = current.next;
/*
由上面一步,使得current移动到了第n + 1个节点,还有length - n - 1个
节点没有走,所以还需要走length - n才可以走完整个链表,此时刚好满足
pre走了length - n步,因此current移动的同时,pre也移动,知道current
为null,此时才满足pre移动length - n步,pre位于删除节点的前一个节点
*/
while(current != null){
current = current.next;
pre = pre.next;
}
pre.next = pre.next.next;//删除倒数第n个节点
/*
这里不应该返回的是head,因为如果删除的是头结点,那么返回head时得到
的链表并没有删除节点,而返回的时root.next,得到的链表才是删除结点之
后的链表
*/
return root.next;
}
}