【题目集03丨LeetCode】链表专项

这篇博客详细介绍了LeetCode中涉及链表的多个问题,包括删除倒数第N个节点、删除链表中的指定节点、删除排序链表的重复元素、旋转链表、两两交换相邻节点、反转链表、反转链表部分、找到相交链表的节点以及环形链表II的解题思路和代码实现。
摘要由CSDN通过智能技术生成

y总讲解原地址

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

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例:
输入1:head = [1,2,3,4,5], n = 2
输出1:[1,2,3,5]
输入2:head = [1,2,3,4,5], n = 2
输出2:[1,2,3,5]
输入3:head = [1,2], n = 1
输出3:[1]
提示:

  • 链表中结点的数目为 sz;
  • 1 <= sz <= 30;
  • 0 <= Node.val <= 100;
  • 1 <= n <= sz。

思路:

  1. 要删除倒数第 n 个结点,首先要让指针指向倒数第 n+1 个结点。
  2. 由于有可能会删除头结点,所以先建立一个虚拟头结点;
  3. first指针先向后走 n 步;
  4. first与second指针同时向后走,当first走到末尾(即first->next==NULL)时终止;
  5. 此时的second指针在要求被删的指针的前一个,因此只要second->next=second->next->next即可。

代码:

/**
 * 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) {
        auto htemp=new ListNode(-1);	//创建虚拟头结点
        htemp->next=head;
        auto first=htemp,second=htemp;
        while(n--) first=first->next;	//先让first走n步
        while(first->next){
            first=first->next;
            second=second->next;
        }
        auto tmp=second->next;
        second->next=tmp->next;
        delete tmp;		//释放删除的空间
        return htemp->next;		//虚拟头结点的next才是head
    }
};

LC237 删除链表中的节点

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点 。
示例:
输入1:head = [4,5,1,9], node = 5
输出1:[4,1,9]
解释1:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
输入2:head = [4,5,1,9], node = 1
输出2:[4,5,9]
解释2:给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
提示:

  • 链表至少包含两个节点。
  • 链表中所有节点的值都是唯一的。
  • 给定的节点为非末尾节点并且一定是链表中的一个有效节点。
  • 不要从你的函数中返回任何结果。

思路:

  1. 单链表真正意义上的删除,必须知道前一个结点才能把当前结点删除。
    由于不知道上一个结点,所以:
  2. 先将当前结点伪装成下一个结点(val=nextVal;next=nextNext);
  3. 然后把下一个结点干掉(该结点可以delete也可以不用delete)。
  4. c++ 的一个特性:*(结构体变量 )表示的是整个结构体的地址(其中包括了val与next),因此可以直接整个结构体进行赋值,即*(node) = *(node->next);

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void deleteNode(ListNode* node) {
        auto tmp=node->next;
        node->val=node->next->val;		//把当前结点伪装成下一个结点
        node->next=node->next->next;
        delete tmp;		//删除下一个结点
    }
};

LC83 删除排序链表中的重复元素

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。
返回同样按升序排列的结果链表。
示例:
输入1:head = [1,1,2]
输出1:[1,2]
输入2:head = [1,1,2,3,3]
输出2:[1,2,3]
提示:

  • 链表中节点数目在范围 [0, 300] 内;
  • -100 <= Node.val <= 100;
  • 题目数据保证链表已经按升序排列。

思路:(考虑两种情况)

  1. 对于多个相同的结点,只保留第一个。
  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* deleteDuplicates(ListNode* head) {
        ListNode* p=head;
        while(p){
        	//要先确保下一个结点不为空
            if(p->next && p->val==p->next->val){
                ListNode* tmp=p->next;
                p->next=p->next->next;
                delete tmp;
            }else p=p->next;
        }
        return head;
    }
};

LC61 旋转链表

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
示例:
输入1:head = [1,2,3,4,5], k = 2
输出1:[4,5,1,2,3]
输入2:head = [0,1,2], k = 4
输出2:[2,0,1]
提示:

  • 链表中节点的数目在范围 [0, 500] 内;
  • -100 <= Node.val <= 100;
  • 0 <= k <= 2 * 1e9。

思路:

  1. 先确定链表长度n;
  2. 如果k>n,会有重复的旋转步骤,因此k%=n
  3. 采用双指针的思想,先让first从头开始走n步,然后再让first与secede同时往后走;
  4. first->next==NULL时,停止;
  5. first->next=head;,让head=second->next; second->next=NULL;

代码:

/**
 * 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* rotateRight(ListNode* head, int k) {
        if(!head) return head;
        int n=0;
        for(auto p=head;p;p=p->next) n++;	//计算链表长度
        k%=n;
        auto first=head,second=head;
        while(k--) first=first->next;
        while(first->next) first=first->next,second=second->next;
        first->next=head;
        head=second->next;
        second->next=NULL;
        return head;
    }
};

LC24 两两交换链表中的节点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
输入1:head = [1,2,3,4]
输出1:[2,1,4,3]
输入2:head = []
输出2:[]
输入3:head = [1]
输出3:[1]
提示:

  • 链表中节点的数目在范围 [0, 100] 内;
  • 0 <= Node.val <= 100。

思路

  1. 由于头结点会改变,设置一个虚拟头结点 p ,方便头结点改变。
  2. 首先:a=p->next; b=a->next;
  3. 循环:p->next=b; a->next=b->next;b->next=a;

代码:

/**
 * 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* swapPairs(ListNode* head) {
        auto htmp=new ListNode(-1);
        htmp->next=head;
        //p的下一个结点与下下一个结点都不为空才可以交换
        for(auto p=htmp;p->next && p->next->next;){
            auto a=p->next,b=a->next;
            p->next=b;
            a->next=b->next;
            b->next=a;
            p=a;
        }
        return htmp->next;
    }
};

LC206 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例:
输入1:head = [1,2,3,4,5]
输出1:[5,4,3,2,1]
输入2:head = [1,2]
输出2:[2,1]
输入3:head = []
输出3:[]
提示:

  • 链表中节点的数目范围是 [0, 5000];
  • -5000 <= Node.val <= 5000。

思路:

  1. 首先让:a=head; b=a->next;
  2. 当b不为空,循环:c=b->next; b->next=a; a=b,b=c;

代码:

/**
 * 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* reverseList(ListNode* head) {
        if(!head) return head;
        auto a=head,b=head->next;
        while(b){
            auto c=b->next;
            b->next=a;
            a=b;
            b=c;
        }
        head->next=NULL;
        head=a;
        return head;
    }
};
  • *

LC92 反转链表 II

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
示例:
输入1:head = [1,2,3,4,5], left = 2, right = 4
输出1:[1,4,3,2,5]
输入2:head = [5], left = 1, right = 1
输出2:[5]
提示:

  • 链表中节点数目为 n;
  • 1 <= n <= 500;
  • -500 <= Node.val <= 500;
  • 1 <= left <= right <= n。

思路:

  1. 首先特判:是否L==R;,如果相等直接返回。
  2. 由于L可能是头结点的位置,所以设立一个虚拟头结点方便计算。
  3. 找到L、R所在的位置,并记录L的前一个结点a,以及R的后一个结点c;
  4. 将L~R进行翻转;
  5. 最后将a->next=R; L->next=c;

代码:

/**
 * 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* reverseBetween(ListNode* head, int left, int right) {
        if(!head || left==right) return head;
        auto htmp=new ListNode(-1);
        htmp->next=head;
        auto a=htmp,n=htmp;
        for(int i=0;i<left-1;i++) a=a->next;	//a要记下m的前一位
        for(int i=0;i<right;i++) n=n->next;
        auto m=a->next,d=n->next;	//d要记下n的后一位
        //翻转链表
        for(auto p=m,q=m->next;q!=d;){
            auto tmp=q->next;
            q->next=p;
            p=q,q=tmp;
        }
        a->next=n;
        m->next=d;
        return htmp->next;
    }
};

LC160 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构。
示例:
输入1:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出1:Intersected at ‘8’
解释1:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

输入2:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出2:Intersected at ‘2’
解释2:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

输入3:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出3:null
解释3:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

提示:

  • listA 中节点数目为 m。
  • listB 中节点数目为 n。
  • 0 <= m, n <= 3 * 1e4;
  • 1 <= Node.val <= 1e5;
  • 0 <= skipA <= m;
  • 0 <= skipB <= n。
  • 如果 listA 和 listB 没有交点,intersectVal 为 0。
  • 如果 listA 和 listB 有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]
    思路:
  1. 假设链表A、B有相交,A=a+c,B=b+c,其中a与b是未相交的部分,c是相交的部分。
  2. 让指针 p 从 headA 开始往后遍历,当走到A的末尾后,从B的开头继续遍历;同理,让指针 q 从 headB 开始遍历,当走到B的末尾后,从A的开头遍历。
  3. p==q时,正好走到了相交的点。(此时的p走过:a+c+b,q走过:b+c+a)
  4. 如果A、B没有相交,即A=a,B=b,同上述走法,当p==q时(此时p==q==NULL),p走过:a+b,q走过:b+a。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        auto a=headA,b=headB;
        while(a!=b){
            if(a) a=a->next;
            else a=headB;
            if(b) b=b->next;
            else b=headA;
        }
        return a;
    }
};

LC142 环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:你是否可以使用 O(1) 空间解决此题?
示例:
输入1:head = [3,2,0,-4], pos = 1
输出1:返回索引为 1 的链表节点
解释1:链表中有一个环,其尾部连接到第二个节点。
输入2:head = [1,2], pos = 0
输出2:返回索引为 0 的链表节点
解释2:链表中有一个环,其尾部连接到第一个节点。
输入3:head = [1], pos = -1
输出3:返回 null
解释3:链表中没有环。
提示:

  • 链表中节点的数目范围在范围 [0, 1e4] 内;
  • -1e5 <= Node.val <= 1e5;
  • pos 的值为 -1 或者链表中的一个有效索引。

思路:

  1. 有两个指针,一个快指针fast每次走两步,一个慢指针slow每次走一步。
  2. 假设链的长度为X,当slow走到环的结点时,fast已经在环中走了X步。
  3. 假设此时,fast离环结点的长度为Y,当fast与slow相遇时,距环结点Y单位远。
  4. 这时,让slow返回头结点遍历,fast与slow都每次只走一步。
  5. 因为,环的长度为X+Y,fast在环Y处,只需再走X步就可以到达结点,而slow从头到环结点也是X步距离。
  6. 当fast==slow时,正好是环结点。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        auto fast=head,slow=head;
        while(fast){
            fast=fast->next;
            slow=slow->next;
            if(fast) fast=fast->next;	//fast每次走两步
            else break;
            if(fast==slow){
                slow=head;		//相遇后slow从头开始走
                while(fast!=slow){
                    fast=fast->next;
                    slow=slow->next;
                }
                return fast;
            }
        }
        return NULL;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值