leetcode 19. 删除链表的倒数第 N 个结点

2023.6.4

leetcode 19. 删除链表的倒数第 N 个结点_第1张图片

         这道题用自己的方法解决的,总体思路就是,先得到链表的总元素数量num,于是就可以知道倒数第N个节点是正数的第多少个节点了,然后再用while循环和一个计数器cur_index找到该节点的位置并删除它。

        值得注意的一点是这里定义了一个虚拟头节点dummy_head指向头节点head,所以此时head节点也沦为一个普通节点了(把头接单当普通节点看就行了,不用对其进行额外操作,这也是我们定义虚拟头节点的目的),再最后返回输出头节点的时候,要返回dummy_head->next,而不是head。这里是易错点,在纸上重新演算的时候才发现的。 接下来上我写的代码:

方法一:计算链表长度(本人)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) 
    {
        ListNode* dummy_head = new ListNode(0);
        dummy_head->next = head;
        ListNode* cur = head;
        int num = 0; //num代表链表中元素数量

        while(cur != nullptr)
        {
            cur = cur->next;
            num++;
        }

        int target = num-n+1;//删除元素的index
        int cur_index = 1;  //当前元素的index
        ListNode* temp1 = dummy_head;
        ListNode* temp2 = dummy_head->next;
        while(temp2 != nullptr)
        {
            if(target == cur_index)
            {
                temp1->next = temp2->next;
                return dummy_head->next;
            }
            cur_index++;
            temp1 = temp1->next;
            temp2 = temp2->next;
        }
        return dummy_head;
    }
};

方法二:栈 

        时间复杂度和空间复杂度都是n。 这方法还是挺妙的,遇到这种需要反转链表的题目可以考虑用栈。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) 
    {
        ListNode* dummy_head = new ListNode(0,head);
        stack stk;
        ListNode* cur = dummy_head;
        while(cur)
        {
            stk.push(cur);
            cur = cur->next;
        }

        for(int i=0; inext = temp->next->next;
        return dummy_head->next;
    }
};

2023.9.22

        时隔三个多月,二刷。 最开始没定义虚拟头节点,会造成一个问题:如果删除的节点是你的头节点head,那最后return的时候就很麻烦,因为你没法返回head,head是被删除的节点。  所以就引入一个虚拟头节点dummy,这样的话head也可以当作普通节点对待了,没有任何特殊性。

        然后由于链表的特性,需要计算出从前往后数需要删除第几个元素,所以需要先统计链表的节点个数。然后维护一个指针指向被删除节点的前一个节点,以便删除指向的节点。 

        代码也是比之前可读性强了很多:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode();
        //定义虚拟头节点
        dummy->next = head;
        ListNode* cur = dummy;
        int count = 0;
        //统计链表节点数量(包括虚拟头节点的数量)
        while(cur)
        {
            count++;
            cur = cur->next;
        }
        cur = dummy;
        int index = count - n - 1; //要将指针停留在被删除节点的前一个节点
        while(index--)
        {
            cur = cur->next;
        }
        cur->next = cur->next->next;
        return dummy->next;
    }
};

你可能感兴趣的:(leetcode专栏,链表,leetcode,数据结构,c++,算法)