LeetCode刷题日志(双指针): 六

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

题目地址

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

题目解析

  • 先分析一下给出的链表的声明

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) {}
 };
  • 无法事先得知链表的总长度

  • 可以考虑将所有的结点的指针按照顺序存放到一个线性表中,通过线性表寻找倒数第n个结点的位置。

代码实现

  • 思路比较简单,直接实现

 
class Solution {
 public:
     ListNode* removeNthFromEnd(ListNode* head, int n) {
         vector<ListNode*> node_ptr_copy;
         ListNode* iter = head;
         while(iter != nullptr){
             node_ptr_copy.push_back(iter);
 ​
             // 结点指针迭代
             iter = iter->next;
         }
         // 此时node_ptr_copy存储了所有结点指针的拷贝
         // 倒数第n个结点实际上是正数第node_ptr_copy.size()+1-n
         // 对应的索引是node_ptr_copy.size()-n
         // 获取目标结点前一结点的指针
         int counts = node_ptr_copy.size();
         if(counts - n == 0){
             // 倒数第n个是首结点,删除首节点,返回下一节点
             ListNode* newHead = head->next;
             delete head;
             return newHead;
         }
         else if(n == 1){
             // 即最后一个元素,修改倒数第二个元素的next并删除最后一个元素即可
             delete node_ptr_copy[counts-n-1]->next;
             node_ptr_copy[counts-n-1]->next = nullptr;
             return head;
         }
         else{
             // 属于中间的元素
             // 令目标的前一个结点的next指向目标后一个结点
             delete node_ptr_copy[counts-n];
             node_ptr_copy[counts-n-1]->next = node_ptr_copy[counts-n+1];
             return head;
         }
     }
 };

结果

 

  • 虽然我们一趟循环就解决了问题,但是结果并不是非常优秀,尝试分析示例代码找到可优化的地方

代码优化

  • 示例代码

 
class Solution {
 public:
     ListNode* removeNthFromEnd(ListNode* head, int n) {
         if(head==NULL) return head;         // 题目说明了链表的大小大于等于1,则不会出现头节点为空的情况,该句可以不需要
         
         ListNode *first=head, *second=head; // 双指针
         // 搜索正数第n+1个结点保存给first
         for(int i=0;i<n;i++){
             if(first==NULL) return head;
             first = first -> next;
         }
         // 正数第n+1个结点是空结点,表示n等于链表长度,意思就是说删除首节点
         if(first==NULL){
             first = head;
             head = head -> next;
             delete first;
             return head;
         }
         // n 小于链表长度,first是正数第n+1个,second是首节点,此时first和second之间的距离就是n,所有现在只需要保持first和second之间的距离,同时向后移动,当first移动到末尾的时候,second也移动到了倒数第n+1的位置。
         while(first->next!=NULL){
             first = first -> next;
             second = second -> next;
         }
         // 此时first不需要了,用于暂存要删除的结点
         first = second -> next;
         second -> next = second -> next -> next;
         delete first;
         return head;
     }
 };

图像解释

 

  • 原理一目了然,只需要注意循环的判定即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学艺不精的Антон

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值