数据结构与算法每日一练---链表---0x0001

环形链表

LeetCode题号:141,142

所属类型:链表

题目

说明

问题一:给你一个链表的头节点head,判断链表中是否有环。
问题二:给定一个链表的头节点head,返回链表开始入环的第一个节点。如果链表无环,则返回null。

题解

  • 链表节点
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:
    bool hasCycle(ListNode *head) 
    {
        if (nullptr == head)
            return false;

        ListNode* pFast = head;
        ListNode* pSlow = head;   

        while (nullptr != pFast && nullptr != pFast->next)
        {
            pFast = pFast->next->next;
            pSlow = pSlow->next;

            if (pFast == pSlow)
                return true;
        }     

        return false;
    }
};
  • 分析:
    采用快慢指针的方式,快指针每次移动两步,慢指针每次移动一步,若两个指针能相遇,则表示链表是环形链表。
    为什么环形链表两个指针一定能相遇?因为快指针每次移动两步,慢指针每次移动一步,这样快指针每次超过慢指针一个节点,当一个一个节点超过的时候,总能遇到,不会错过(非环形链表pFast会先走到最后一个节点,也就是next为nullptr或者 nullptr结束循环)

问题二

class Solution 
{
public:
    ListNode *detectCycle(ListNode *head) 
    {
        if (nullptr == head)
            return nullptr;

        ListNode* pFast = head;                                 // 快指针
        ListNode* pSlow = head;                                 // 慢指针

        while (nullptr != pFast && nullptr != pFast->next)
        {
            pFast = pFast->next->next;
            pSlow = pSlow->next;

            // 快慢指针能相遇,那么说明是环形链表
            if (pFast == pSlow)
            {
                ListNode* pNodeA = pFast;       // 相遇节点
                ListNode* pNodeB = head;        // 头结点
                while (pNodeA != pNodeB)
                {
                    pNodeA = pNodeA->next;
                    pNodeB = pNodeB->next;
                }

                return pNodeA;
            }
                
        }     

        return nullptr;
    }
};
  • 分析:
    判断链表是否为环形的逻辑如上所示,此时我们来定义几个节点名称
  • 头结点 H(Head)
  • 环形链表入环第一个节点 C(Circle)
  • 快慢指针相遇节点 M(Meet)
    再定义几个长度(每次一个节点):
    H->C:需要走x次
    C->M:需要走y次
    M->C:需要走z次

当快慢指针相遇时:2(x+y) = x+y+n(y+z) (备注:n可能为1,2,3…)
快指针行程:x+y+n(y+z)
慢针行程:x+y

由上式继续推导:
x+y = n(y+z)
x = (n-1)(y+z) + z

当 n = 1 时,从H->C 和 从M->C相等
当 n = 2 时,从H->C 和 从M->C再加1圈相等
当 n = 3 时,从H->C 和 从M->C再加2圈相等

所以,当一个指针从头节点出发,另外一个指针从相遇节点出发,它们总能在入口节点相遇。

if (pFast == pSlow)
{
    ListNode* pNodeA = pFast;       // 相遇节点
    ListNode* pNodeB = head;        // 头结点
    while (pNodeA != pNodeB)
    {
        pNodeA = pNodeA->next;
        pNodeB = pNodeB->next;
    }

    return pNodeA;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值