链表OJ题讲解2

💓博主个人主页:不是笨小孩👀
⏩专栏分类:数据结构与算法👀
🚚代码仓库:笨小孩的代码库👀
⏩社区:不是笨小孩👀
🌹欢迎大家三连关注,一起学习,一起进步!!💓


在这里插入图片描述

链表的回文结构

在这里插入图片描述

回文节后是对称的,所以这个题目我们可以先找到中间节点,然后将中间反转,然后两个指针,一个从头开始,一个从反转后的头节点开始,一一比对,如果val不相等一定不是回文,如果相等,那么一定是回文链表。

偶数个数据时
在这里插入图片描述
奇数个数据时:
在这里插入图片描述
所以我们可以清楚的知道当rmid为空时就要结束循环。
代码如下:

// 计算中间节点
struct ListNode* midNode(struct ListNode* head)
{
    struct ListNode* fast = head,*slow = head;
    while(fast&&fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

// 翻转链表
struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* cur = head, *newhead = NULL,*next = NULL;
    while(cur)
    {
        next = cur->next;
        cur->next = newhead;
        newhead = cur;
        cur = next;
    }
    return newhead;
}
bool isPalindrome(struct ListNode* head)
{
    //计算中间节点
    struct ListNode* mid = midNode(head);
    //反转链表
    struct ListNode* rmid =reverseList(mid);
    while(rmid)
    {
        //不相等一定不回文,返回false
        if(head->val!=rmid->val)
        {
            return false;
        }
        head = head->next;
        rmid = rmid->next;
    } 
    //循环结束一定是回文
    return true;
}

链表分割

在这里插入图片描述

这个题我们需要两个链表,然后遍历原链表,将小于x的尾插到head1,将大于等于x的尾插带head2,然后将两个链表链接一下,需要注意的是我们需要将head2的为尾节点的next置空,不然可能会出现带环链表,还有就是我们的head1有可能为空,这里我们使用带哨兵位的链表,就可以解决这个问题,但是head2也可能为空,这时我们就需要将head1的尾节点next置空,不然就会存在非法访问。

代码如下:

struct ListNode* partition(struct ListNode* head, int x)
{
    struct ListNode* head1,*tail1,*head2,*tail2;
    //开辟两个哨兵位
    head1 = tail1 = (struct ListNode*)malloc(sizeof(struct ListNode));
    head2 = tail2 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* cur = head;
    while(cur)
    {
        if(cur->val<x)
        {
            //小于的尾插到tail1
            tail1->next = cur;
            cur = cur->next;
            tail1 = tail1->next;
        }
        else
        {
            //大于的尾插到tail2
            tail2->next = cur;
            cur = cur->next;
            tail2 = tail2->next;
        }
    }
    //链接两个链表
    tail1->next = head2->next;
    //防止出现带环链表
    tail2->next = NULL;
    if (head2->next == NULL)
    {
        //第二个链表为空,这是要将这是要将tail1->next置空,不然会出现非法访问
        tail1->next = NULL;
        struct ListNode* next = head1->next;
        free(head2);
        free(head1);
        return next;
    }
    //保存头结点
    struct ListNode* next = head1->next;
    //释放哨兵位
    free(head1);
    free(head2);
    return next;
}

两个链表的交集

在这里插入图片描述

判断是否相交非常简单,我们只需要判断两个链表的为节点是否相同就可以了,如果相同,那么它们一定是相交的,我们就可以找相加的那个节点,我们在找尾的时候可以记录一下两个链表的长度,由于从相交的位置开始后面的节点都一样了,我们可以计算出长的那个链表比短的链表长多少,让长的链表先走多少步,然后让两个链表同时走,相等的第一个节点就是我们要求的。

代码如下:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
    struct ListNode* curA = headA,*curB = headB;
    int s1 = 1;
    int s2 = 1;
    //找A链表的尾,顺表记录长度
    while(curA->next)
    {
        curA= curA->next;
        s1++;
    }
    //找B链表的尾,顺表记录长度
    while(curB->next)
    {
        curB= curB->next;
        s2++;
    }
    //尾节点不相等一定不相交
    if(curA!=curB)
    {
        return NULL;
    }
    //计算差值
    int k = abs(s1-s2);
    //假设长的链表为A
    struct ListNode* longNode = headA,*shortNode = headB;
    //如果B链表长,则将长链表赋值B
    if(s1<s2)
    {
        longNode = headB;
        shortNode = headA;
    }
    //长链表先走k步
    while(k--)
    {
        longNode= longNode->next;
    }
    //寻找第一个相等的节点
    while(longNode!=shortNode)
    {
        longNode = longNode->next;
        shortNode = shortNode->next;
    }
    return longNode;
}

链表循环

在这里插入图片描述

我们可以用快慢指针来解决这个问题,我们让慢指针走一步,快指针走两步,如果有环,那么当俩个指针都进入环中是,就是追击问题,如果快指针走到NULL,或者快指针的next为空,那么就意味着链表无环。

代码如下:

bool hasCycle(struct ListNode *head) 
{
    struct ListNode* fast = head,*slow = head;
    while(fast&&fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast==slow) return true;
    }
    return false;
}

思考

1.如果slow走一步,fast走二步是不是一定可以追上?

答案是一定可以追上,为什么呢,假设slow进环时fast与它相距N步,那么每走一步,他们之间的距离减少一,而N一定小于一圈的长度,所以在一圈之内fast一定追上slow。

在这里插入图片描述
2.如果slow走一步,fast走三步是不是一定可以追上?

答案是不太一定,假设他们之间的距离还是N,如果N是偶数那么就追上了,如果N奇数,他们就会错过,假设圆的长度为C,他们的距离就会变成C-1,这是又要分情况了,假设C-1位奇数那么就一定追不上了,如果C-1位偶数那么在下一圈的追击中就会追上。

在这里插入图片描述

链表周期 II

在这里插入图片描述

方法一

我们需要推导一个公式,假设头到入环点的长度为L,然后假设他们在环中相遇的点为X,入环点到X的距离为N,然后整个环的长度为C,我们可以推导:

在这里插入图片描述

有了这个推导关系我们就可以写代码了。
代码如下:

struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode* slow = head,*fast = head;

    while(fast&&fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        //判断是否相遇
        if(fast==slow)
        {
            struct ListNode* meet = fast,*cur = head;
            //一个从相遇点开始走,一个从头开始走
            while(meet!=cur)
            {
                meet = meet->next;
                cur = cur->next;
            }
            //此时相遇的点就是入环点
            return meet;
        }
    }
    //出循环一定没有环
    return NULL;
}

方法二

我们还是找相遇点,然后我们把相遇点的next置空,保存相遇点的next,然后就变成了相交链表。

在这里插入图片描述

代码如下:

//找相交链表的第一个相交点
 struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
    struct ListNode* curA = headA,*curB = headB;
    int s1 = 1;
    int s2 = 1;
    //找A链表的尾,顺表记录长度
    while(curA->next)
    {
        curA= curA->next;
        s1++;
    }
    //找B链表的尾,顺表记录长度
    while(curB->next)
    {
        curB= curB->next;
        s2++;
    }
    //尾节点不相等一定不相交
    if(curA!=curB)
    {
        return NULL;
    }
    //计算差值
    int k = abs(s1-s2);
    //假设长的链表为A
    struct ListNode* longNode = headA,*shortNode = headB;
    //如果B链表长,则将长链表赋值B
    if(s1<s2)
    {
        longNode = headB;
        shortNode = headA;
    }
    //长链表先走k步
    while(k--)
    {
        longNode= longNode->next;
    }
    //寻找第一个相等的节点
    while(longNode!=shortNode)
    {
        longNode = longNode->next;
        shortNode = shortNode->next;
    }
    return longNode;
}

struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode* slow = head,*fast = head;

    while(fast&&fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        //判断是否相遇
        if(fast==slow)
        {
            struct ListNode* meet = fast,*newhead = fast->next;
            //转换相交链表
            meet->next = NULL;
            //找第一个相交点,也就是入环点
            return getIntersectionNode(head,newhead);
        }
    }
    //出循环一定没有环
    return NULL;
}

总结一下:

方法一逻辑强,不好推导,但是推导出来代码好写
方法二逻辑没有那么强,适合我们普通人,但是代码量稍微大一点。

今天的分享就到这里结束了,感谢大家的关注和支持。

  • 78
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 133
    评论
评论 133
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不是笨小孩i

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

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

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

打赏作者

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

抵扣说明:

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

余额充值