LeetCode练习5

1、链表的中间节点

876. 链表的中间结点

难度简单479收藏分享切换为英文接收动态反馈

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

提示:

  • 给定链表的结点数介于 1100 之间。

题解:

  1. 数组法:链表的缺点在于不能通过下标访问对应的元素。遍历链表,同时将遍历到的元素依次放入数组 A 中。如果我们遍历到了 N 个元素,那么链表以及数组的长度也为 N,对应的中间节点即为 A[N/2]
  2. 单指针,遍历两边链表,第一次遍历时,我们统计链表中的元素个数 N;第二次遍历时,我们遍历到第 N/2 个元素时,将该元素返回即可
  3. 经典做法:利用快慢双指针,当快指针走两步,慢指针走一步,而快指针满足当前非空和下一个节点非空时可前进,否则停下,此时即慢指针刚刚好到达中间位置或者偶数的中间位置的第二个数字

图解如下:

在这里插入图片描述

代码如下:

/**
 * 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* middleNode(ListNode* head) {
        ListNode* slow = head;
        ListNode* fast = head;
        while (fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
};

2、删除链表倒数第N个结点

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

难度中等1764收藏分享切换为英文接收动态反馈

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

示例 1:

img

输入: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

**进阶:**你能尝试使用一趟扫描实现吗?解: 递归思想

**题解:**首先本体因为需要对头结点进行返回,一般常用的技巧是添加一个哑节点(dummy node),它的next 指针指向链表的头节点。这样一来,我们就不需要对头节点进行特殊的判断了。

  1. 添加哑结点,很容易就想到第一种解法就是将链表进行扫一遍,并且记录长度L,接着再从头遍历,当遍历到L-N+1个节点时(添加了哑节点),它的下一个节点便是我们需要删除的,然后将其删除即可(将其next指向下下个节点)。

  2. 快慢双指针法,添加哑结点,然后创建快慢指针,由于我们需要找到倒数第 nn 个节点,因此我们可以使用两个指针 fast和slow同时对链表进行遍历,并且fast比 slow超前 n 个节点。当fast 遍历到链表的末尾时,slow就恰好处于倒数第 n 个节点。

    具体地,初始时 fast指向头节点,slow指向新的虚拟哑结点。我们首先使用fast对链表进行遍历,遍历的次数为 n(与慢指针间隔为n个结点)。此时,fast 和slow之间间隔了 n 个节点,即 fast 比 slow超前了 n 个节点。

    在这之后,我们同时使用fast 和slow对链表进行遍历。当 slow遍历到链表的末尾(即 fast为空指针)时,slow恰好指向倒数第 n -1个节点(即所需要删除结点的前结点),然后将其指针指向下下个结点即可。

    具体可如图:

在这里插入图片描述

代码如下:

方法一:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        int Length=0;
        ListNode* dummy = new ListNode(0, head);//创建一个虚拟的头节点,dummy的下一个结点就是head
        ListNode* begin=head;
        ListNode* end=dummy;
        while(begin!=NULL){
            begin=begin->next;
            Length++;
        }
        int flag=Length-n;
        for(int i=0;i<flag;i++)
             end=end->next;
        end->next=end->next->next;//到达所需要删除结点的前节点,将其指针指向下下个结点
        
    return dummy->next;//返回头指针
    }
};

方法二:

class Solution {
public:
    ListNode *removeNthFromEnd(ListNode *head, int n) {
        ListNode *dummy = new ListNode(0, head);//创建一个虚拟的头节点,dummy的下一个结点就是head
        ListNode *fast = head;
        ListNode *slow = dummy;
        for (int i = 0;i<n;i++){
            fast = fast->next;
        } // 此时 slow 与 fast 之间距离为 n + 2
        while (fast) {
            fast = fast->next;
            slow = slow->next;
        } // 此时 fast 为原链表后的 slow 指向删除节点的前一个
        slow->next = slow->next->next;
        return dummy->next;
    }
};

解题总结:这种双指针,一般注意快慢指针,并且注意间隔。这种倒数第几个甚至找中间结点都需要快走几步或者间隔多少个,往往快指针到尾部了,慢指针就到合适地方。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
嗨!很高兴回答你的问题。LeetCode是一个非常受欢迎的技术面试准备平台,提供了大量的算法和数据结构题目。以下是一些LeetCode的指南和建议: 1. 熟悉常见题型:LeetCode上的题目可以分为不同的类别,如数组、字符串、链表、树、图等。首先建议你熟悉常见的题型和相关的算法思想,比如双指针、动态规划、回溯等。 2. 解题思路和技巧:对于每个题目,理解问题的要求并且设计一个合适的算法解决它是很重要的。可以通过阅读题目描述、示例和约束条件来确定解决问题的最佳方法。 3. 刷题和练习:刷LeetCode的题目是提高编程能力和算法思维的有效方式。开始时可以选择一些简单的题目,逐渐增加难度。保持刷题的节奏,坚持练习可以加深对不同问题类型的理解和掌握相应的解题技巧。 4. 学习他人的解法:在LeetCode上,每个题目都有多种解法。尝试理解其他人的解法,学习他们的思路和技巧。这有助于拓宽自己的解题思路,提高代码质量。 5. 思考时间和空间复杂度:在解题过程中要注意时间和空间复杂度的优化。理解算法的时间和空间复杂度有助于评估解决方案的效率,并且在面试过程中也是一个重要的考察点。 6. 阅读和参与讨论:LeetCode上有许多用户提交的解答和讨论。可以阅读他人的解法,学习他们的想法和技巧。同时,积极参与讨论,向其他人提问并分享自己的解决思路,可以加深对问题的理解。 总之,刷LeetCode是提高编程能力和算法思维的好方法,但不要只局限于刷题本身,要时刻保持学习和思考。祝你在LeetCode的刷题过程中取得进步!如果你有其他问题,我会很乐意回答。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值