LeetCode 19. 删除链表的倒数第 N 个结点【知识点:快慢指针、递归、堆栈】

这篇博客介绍了如何在单链表中删除倒数第N个节点的四种方法,包括计算链表长度、前后双指针、堆栈和递归。每种方法都有其独特之处,其中前后双指针法被认为是最佳解决方案,因为它只需要一次遍历。此外,博客还提供了Java代码示例和测试用例,帮助读者深入理解这个问题。
摘要由CSDN通过智能技术生成

题目

给你一个链表,删除链表的倒数第 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

进阶: 你能尝试使用一趟扫描实现吗?

来源:力扣(LeetCode)
链接:leetcode 19. 删除链表的倒数第 N 个结点
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题

方法一:顺着题意理解,计算链表长度

先遍历一遍整个链表计算链表长度L,再遍历链表到需要删除的结点的前一个结点:删除的结点是第L-n+1个,遍历到L-n停下

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeNthFromEnd(struct ListNode* head, int n)
{
    struct ListNode dummyHead;
    dummyHead.next = head;
    dummyHead.val = 0; //储存链表元素个数
    struct ListNode* p;

    for(p=head;p;p=p->next,dummyHead.val++);

    int i = 0;
    for(p=&dummyHead;i < dummyHead.val-n;p=p->next,i++);

    struct ListNode* temp = p->next;
    p->next = temp->next;
    free(temp);

    return dummyHead.next;
}

方法二:前后双指针(本题最优解)(只需一次遍历)

双指针p,q 初始化指向虚拟表头;前指针q先走,当p,q之间间隔n个结点时,p,q都同时往后走;直到q遍历完,此时p所指的即为需要删除结点的前一个结点;
在这里插入图片描述

struct ListNode* removeNthFromEnd(struct ListNode* head, int n)
{
    struct ListNode dummyHead;
    dummyHead.next = head;
    
    struct ListNode *front,*behind;
    front = behind = &dummyHead;

    int TheIntervalBetweenTwoPointers = -1;
    while(behind){

        if(TheIntervalBetweenTwoPointers != n){

            behind = behind->next;
            TheIntervalBetweenTwoPointers++;
        }else{

            front = front->next;
            behind = behind->next;
        }
    }

    struct ListNode* temp = front->next;
    front->next = temp->next;
    free(temp);

    return dummyHead.next;
}

方法三:堆栈

由于是要删除的按倒数来的,很容易联想到堆栈,后进先出,将整个链表一次入栈,再出栈 寻找需要删除的结点的前结点;

数组堆栈:

typedef struct _Stack{
    struct ListNode* A[31];
    int Top;
}Stack;

void Push( Stack* S,struct ListNode* p )
{
    S->Top++;
    S->A[S->Top] = p;
}

struct ListNode* Pop( Stack* S )
{
    return S->A[S->Top--];
}

struct ListNode* removeNthFromEnd(struct ListNode* head, int n)
{
    struct ListNode dummyHead;
    dummyHead.next = head;
    
    Stack* S = malloc(sizeof(Stack));
    S->Top = -1;

    struct ListNode* p = &dummyHead;
    while(p){

        Push( S,p );
        p = p->next;
    }

    while(n--){
        Pop( S );
    }

    p = Pop( S );
    struct ListNode* temp = p->next;
    p->next = temp->next;
    free(temp);
    free( S );

    return dummyHead.next;
}

链式堆栈:

方法四:递归

另一个按倒数来的方法就是递归了,事实上链表非常适合递归,比如递归在二叉树中的应用

struct ListNode* removeNthFromEnd(struct ListNode* head, int n)
{
    static int K = 0;

    if(head->next){
        head->next = removeNthFromEnd( head->next,n );
    }

    K++;

    if(K == n){
        struct ListNode* temp = head->next;
        free(head);
        return temp;
    }else{
        return head;
    }
}

测试用例:[1] 1 单独测试时,没有问题;但是提交的时候就是通不过,奇怪

参考别人的java代码:

class Solution {
    int i=0;
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head.next!=null)
            head.next=removeNthFromEnd(head.next,n);
        i++;
        if(i==n)
            return head.next;
        else return head;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值