判断链表是否带环?若带环求环的长度?若带环求环的入口点?

链表就跟他的名字一样,是一串串起来的链,具体是什么样子在程序中是不能直观的看到的,因为每个节点的地址并不是连续的,但是我们可以通过指针,将其进行遍历访问。
今天我们来讨论一下,一个链表是否带环?若带环,那么环的长度又是多少?环的入口?

一个链表是否带环?

第一次拿到这道题的时候,我的函数原型是这样的

bool isCircle(ListNodeT* head)

判断一个链表是否带环,返回值最直接的应该就是bool,在C++中bool即真假判断。
第二版

ListNodeT* isCircle(ListNodeT* head)

因为如果带环的话,后面可能还要进行相应的使用,所以我想到了ListNodeT*作为返回值,这样可以得到是否带环的判断结果,又可以拿到带环的节点
网上有一种用到了pair

pair<Node<int>*, bool> isCircle(ListNodeT* head)

pair使用的原则,应该是上面两者的综合吧。

设置节点

template<class T>
struct ListNode
{
    T _data;
    ListNode<T>* _next;
    ListNode(T x = T())
        :_data(x)
        , _next(NULL)
    {}
};

为了统一,做typedef

typedef ListNode<int> ListNodeT;

判断是否带环

ListNodeT* isCircle(ListNodeT* head)
{
    if (head == NULL)
    {
        printf("is no node in this list\n");
        return NULL;
    }
    ListNodeT* first = head;
    ListNodeT* second = head;
    if (head->_next == NULL)
    {
        printf("List isn't have circle\n");
        return NULL;
    }
    while (first->_next)
    {
        first = first->_next;
        second = second->_next->_next;
        if (first == second)
        {
            printf("List hava a circle\n");
            return first;
        }
    }
    printf("List isn't have circle\n");
    return NULL;
}

通过快慢指针法,判断是否带环,好比跑步,假设两个人在跑步,且速度不变,一个快,一个慢,则在一定时间后,两个人肯定会再次相遇。

求带环环的长度

int length(ListNodeT* head,ListNodeT* node)
{
    ListNodeT* cur = node->_next;
    int count = 1;
    while (cur != node)
    {
        count++;
        cur = cur->_next;
    }
    return count;
}

判断带环函数返回的节点肯定是在换中的,当一个节点一直走,相遇的节点不动是,再次相遇,就可以求得环的长度

求环的入口

ListNodeT* FindLoopPort(ListNodeT*head, ListNodeT* node)
{
    ListNodeT* cur = head;
    while (cur != node)
    {
        cur = cur->_next;
        node = node->_next;
    }
    return cur;
}

这里是一个数学公式的实践,交点node到入口点的距离=头指针到连接点的距离,因此,分别从交点、头指针开始走,相遇的那个点就是连接
假设链表总长为L,头节点到入口点的距离为a,入口点到快慢指针交点距离为x,环的长度为R,现在假设慢指针走了S步与快指针相遇,
s = a + x;
那么快指针走了2S步,
2s = a + nr + x;
就可以得到:a + x = nr;
->a = nr - x;
可以看出来,头节点到入口点的距离等于,交点到入口点的距离,那我们让两个指针,一个从交点走,一个从头节点走,最后一定在入口点相遇。
测试

int main()
{
    ListNodeT* head = new ListNodeT(1);
    ListNodeT* node1 = new ListNodeT(2);
    ListNodeT* node2 = new ListNodeT(3);
    head->_next = node1;
    node1->_next = node2;
    node2->_next = head;
    ListNodeT* ret = NULL;
    ret = isCircle(head);
    if (ret !=NULL)
        cout << length(head,ret) << endl;
    if (ret != NULL)
        cout << FindLoopPort(head, ret) << endl;
    ListNodeT* node = FindLoopPort(head, ret);
    cout << node->_data << endl;
    system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值