美文网首页
19 删除链表的倒数第 N 个结点

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

作者: 一枚懒人 | 来源:发表于2021-12-21 20:30 被阅读0次

题目:

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例1
示例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]

自行解答:

算法思路:

其实算法是链表中常用的经典算法,双指针或快慢指针

1:初始化2个指针,分别是first 和second

2:快指针先移动,在等待n回合之后,慢指针和快指针一起开始移动,当快指针移 动到最后一个元素时间,移动停止

3:开始删除节点(n是指链表的倒数第n个节点)

3.1:如果在循环结束时间,慢指针还在头结点,且n大于循环的次数,此时需要删除头结点

3.2:如果在循环结束时间,慢指针还在头结点,且n等于循环的次数,此时慢指针的下一个就是需要删除的元素

3.3:慢指针离开头结点的时候,直接删除满指针后面的元素即可

4:返回删除节点后的头结点

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) {}
};
void deleteMiddleNode(const ListNode *firstPointer, ListNode *secondPointer) {
    ListNode *beforeRemove = secondPointer;
    ListNode *needRemove =  secondPointer->next;
    ListNode *afterRemove;
    if(needRemove == firstPointer){
        beforeRemove->next = nullptr;
    } else {
        afterRemove = needRemove->next;
        beforeRemove->next =afterRemove;
    }
}

ListNode* removeNthFromEnd(ListNode* head, int n) {

    if(n==1 && head->next == nullptr){
        return nullptr;
    }
    //1:初始化快慢指针
    ListNode *firstPointer;
    firstPointer = head;//快指针
    ListNode *secondPointer;
    secondPointer = head;//慢指针
    ListNode * result = head;
    int count = 0;
    //2:移动快慢指针
    while (firstPointer->next!= nullptr){
        if(count >= n){
                secondPointer = secondPointer->next;
        }
        firstPointer = firstPointer->next;
        count++;
    }
    if(secondPointer == head ){
      //3.1在慢指针还在头结点的前提下,移除头结点  
      //删除头节点
        if(n>count){
            result = head->next;
        }
        //3.2在慢指针还在头结点的前提下,移除中间结点  
        //删除中间节点的
        if(n==count){
            deleteMiddleNode(firstPointer, secondPointer);
        }
    } else{
      //3.3在慢指针已经移动到中间节点了,删除慢指针后面的节点
        deleteMiddleNode(firstPointer, secondPointer);
    }
  //4:返回删除节点之后的头结点
    return result;
}

int main() {
    ListNode l1(1);
    ListNode l2(2);
    ListNode l3(3);
    ListNode l4(4);
    ListNode l5(5);
    l1.next = &l2;
    l2.next = &l3;
    l3.next = &l4;
    l4.next = &l5;
    ListNode *result = removeNthFromEnd(&l1,2);
    cout << "result head"<<result->val<<endl;
    return 0;
}

算法分析:

时间复杂度:O(N),N是链表的长度,因为只循环了一趟

空间复杂度:O(1),只使用了有限变量,未使用额外的空间

官方解答:

官方思路1:

采用栈的结构实现,即遍历一遍链表,顺序遍历时每个元素都入栈,当出栈时,开始计数,删除出栈的第n个元素即可

采用栈算法实现时,算法的时间复杂度为O(N),N是链表的长度,空间复杂度也是O(N),因为采用了栈来额外存储,具体代码如下:

 ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(0, head);
        stack<ListNode*> stk;
        ListNode* cur = dummy;
        while (cur) {
            stk.push(cur);
            cur = cur->next;
        }
        for (int i = 0; i < n; ++i) {
            stk.pop();
        }
        ListNode* prev = stk.top();
        prev->next = prev->next->next;
        ListNode* ans = dummy->next;
        delete dummy;
        return ans;
    }

官方思路2:

采用也是快慢指针来实现,但是使用了一个小的技巧,即在入参传入之后,重新给链表指定个一个头结点dummy,这样就避免了,删除头结点的时候的场景识别,我们永远返回的头结点都是dummy.next,非常使用的小技巧,时间空间复杂度和自行解答中的一样

ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(0, head);
        ListNode* first = head;
        ListNode* second = dummy;
        for (int i = 0; i < n; ++i) {
            first = first->next;
        }
        while (first) {
            first = first->next;
            second = second->next;
        }
        second->next = second->next->next;
        ListNode* ans = dummy->next;
        delete dummy;
        return ans;
}

相关文章

网友评论

      本文标题:19 删除链表的倒数第 N 个结点

      本文链接:https://www.haomeiwen.com/subject/nztfqrtx.html