【力扣中链表的一些题目】

有关链表的一些题目



前言

记录一下经常出错或者思路比较巧妙的链表题目。用于自己之后的查看。


一、Leetcode19. 删除链表的倒数第 N 个结点

1.题目描述

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

在这里插入图片描述

2.求解思路

  • 只遍历一次链表,找出倒数第 n 个节点方法:(假设该链表共有N个节点)
    (1)首先,我们先让一个指针 p1 指向链表的头节点 head,然后走n步;
    (2)现在的 p1,只要再走 N-n 步,就能走到链表末尾的空指针了
    (3)趁这个时候,再用一个指针 p2 指向链表头节点 head,让 p1 和 p2 同时向前走,p1 走到链表末尾的空指针时前进了 N-n 步,p2 也从 head 开始前进了N-n 步,停留在第 N-n+1 个节点上,即恰好停链表的倒数第 n个节点上。
  • 但是在本题中,由于需要删除掉倒数第n个节点,因此需要让指针指向倒数第n+1个节点的位置。
  • 另外,由于题目中说的倒数第n个节点又可能是头节点,为了统一写法(不单独判断是不是头节点),需要设置虚拟头节点,因此,两个快慢指针应该都从虚拟头节点开始走,其他的不用改变,最终还是会找到倒数第n-1个节点。
  • 注意!返回的时候也应该返回虚拟头节点的下一个节点。

3.代码

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy=new ListNode(0);//要设置一个虚拟头节点,因为有可能链表只有一个节点,被删除的节点为头节点
        dummy->next=head;
        ListNode* temp=dummy;//必须从虚拟头节点开始走!!(快指针)因为有可能被删除的节点为头节点
        for(int i=0;i<n+1;i++){//要删除倒数第n个节点,所以要让慢指针指向倒数第n+1个节点,这样才能做删除操作
            temp=temp->next;
        }
        ListNode* cur=dummy;//必须从虚拟头节点开始走!!(慢指针)因为有可能被删除的节点为头节点
        while(temp!=nullptr){
            cur=cur->next;
            temp=temp->next;
        }
        ListNode* t=cur->next;
        cur->next=t->next;
        delete t;
        return dummy->next;//返回的时候要返回虚拟头节点的下一个节点,不能直接返回head,因为head有可能被删除了
    }
};

二、Leetcode160. 相交链表

1.题目描述

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

在这里插入图片描述

2.求解思路

有两种思路:

  1. 分别计算出两个链表的长度,然后为了将他们的尾部对齐,即为了让两个指针同时遍历它们时能够同时到达尾节点(因为若是两个链表相交,他们最后一个或几个节点一定是一样的),那么需要让指针先遍历那个较长的链表,先走的长度为两链表的长度差值。然后再同时移动两个指针,直到两个指针相等则表示两链表相交,若指针指向了nullptr,则表示不相交。
  2. 同时遍历两个链表A和B,遍历A的那个指针在遍历完A之后再去遍历B,同理,遍历B的那个指针在遍历完B之后再去遍历A。如果遍历过程中两指针相等则表示两链表相交,若指针指向了nullptr,则表示不相交。(参考labuladong)
    在这里插入图片描述
    注意!最终要判断两个指针是否相等,而不是他们指向的值是否相等。

3.代码

思路1代码如下:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int a=0,b=0;
        ListNode* A=headA;
        ListNode* B=headB;
        while(A!=nullptr){
            a++;
            A=A->next;
        }
        while(B!=nullptr){
            b++;
            B=B->next;
        }
        if(a>b){
            swap(headB,headA);
            swap(a,b);
            /*ListNode* temp=headA;
            headA=headB;
            headB=temp;
            int n=a;
            a=b;
            b=n;*/
        }
        int n=b-a;
        while(n){
            headB=headB->next;
            n--;
        }
        while(headA!=nullptr){
            if(headA==headB) return headA;
            else{
                headA=headA->next;
                headB=headB->next;
            }
        }
        return NULL;
    }
};

思路2代码如下:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {//很巧的一个思路
        ListNode* p1=headA;
        ListNode* p2=headB;
        while(p1!=p2){
            if(p1==nullptr) p1=headB;
            else p1=p1->next;
            if(p2==nullptr) p2=headA;
            else p2=p2->next;
        }
        return p1;
    }
};

三、Leetcode86. 分隔链表

1.题目描述

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

在这里插入图片描述

2.求解思路

这里需要分解让你把原链表一分为二,可以把原链表分成两个小链表,一个链表中的元素大小都小于 x,另一个链表中的元素都大于等于 x,最后再把这两条链表接到一起,就得到了题目想要的结果。(参考labuladong)

创建链表的时候要注意先创建虚拟头节点,返回的时候也要返回虚拟头节点的下一个节点。

3.代码

代码如下:

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        ListNode* dummy1=new ListNode(0);
        ListNode* dummy2=new ListNode(0);
        ListNode* p1=dummy1;
        ListNode* p2=dummy2;
        ListNode* p=head;
        while(p!=nullptr){
            if(p->val<x){
                p1->next=p;
                p1=p1->next;
            }
            else{
                p2->next=p;
                p2=p2->next;
            }
            ListNode* temp=p->next;
            p->next=nullptr;
            p=temp;
        }
        ListNode* cur=dummy2->next;
        dummy2->next=nullptr;
        p1->next=cur;
        return dummy1->next;
    }
};

总结

以上介绍了几个易错的链表题目,用于自己之后查看。注意虚拟头节点的建立!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值