Leetcode-142: Linked List Cycle II

这题非常经典,其实没见过的话短时间内想出空间复杂度O(1)的解法我觉得不容易。
解法1:用set或map。但要是要求空间复杂度O(1)就不行了。

ListNode *detectCycle(ListNode *head) {

    if (!head) return NULL;
    set<ListNode *> s;

    ListNode* p = head;
    while(p) {
        if (s.count(p)) {
            return p;
        }
        else {
            s.insert(p);
            p=p->next;
        }
    }

    return NULL;
}

解法2:还是快慢指针法,我是参考网上的解法。
这里写图片描述

如图所示,p1和p2都从head出发,p1每次走1步,p2每次走2步,那么当p1和p2在环中某处相遇时,p2已经走s+m+kr, p1走了s+m+lr,也就是说相遇的时候p2和p1所花次数一样,但p2比p1多走了k-l圈。此处l>=0,k>=1, r为一圈周长。
另外我们知道p2走的路应该是p1的两倍,那么有s+m+kr=2(s+m+lr),可推出s+m=(k-2l)r。设n=k-2l,这个n圈实际上就是s+m的距离。

那么我们有s=nr-m。这个说明什么呢?说明如果让p1从head开始,p2从meet place开始,两个每次都只走一步,则p1走了s步,p2走了nr-m步,两者刚好相遇在环的起始点。

代码如下:

#include <iostream>
#include <set>

using namespace std;

struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

ListNode *detectCycle(ListNode *head) {

    if (!head) return NULL;
    ListNode *p1=head, *p2=head;
    while(p2) {
        p1=p1->next;
        p2=p2->next;
        if (!p2 || !p2->next) return NULL;
        p2=p2->next;

        if (p2==p1)
            break;
    }
    p1=head;
    while(p1 && p2 && p1!=p2) {
        p1=p1->next;
        p2=p2->next;
    }

    return p1;
}

int main()
{
    ListNode a=ListNode(1);
    ListNode b=ListNode(2);
    ListNode c=ListNode(3);
    ListNode d=ListNode(4);
    ListNode e=ListNode(5);
    a.next=&b;
    b.next=&c;
    c.next=&d;
    d.next=&e;
    //e.next=&c;

    ListNode* p = detectCycle(&a);
    if (p)
        cout<<p->val<<endl;

    return 0;
}

我的另一个类似解法如下:

/**
 * Definition of singly-linked-list:
 * class ListNode {
 * public:
 *     int val;
 *     ListNode *next;
 *     ListNode(int val) {
 *        this->val = val;
 *        this->next = NULL;
 *     }
 * }
 */

class Solution {
public:
    /**
     * @param headA: the first list
     * @param headB: the second list
     * @return: a ListNode
     */
    ListNode * getIntersectionNode(ListNode * headA, ListNode * headB) {

        if (!headA || !headB) return NULL;
        
        ListNode * nodeA = headA;
        ListNode * endA = NULL;
        
        while(nodeA && nodeA->next) {
            nodeA = nodeA->next;
        }
        endA = nodeA;
        endA->next = headA;  // construct a circle
        
        ListNode * node1 = headB;
        ListNode * node2 = node1->next;
        ListNode * meetNode = NULL;
        
        while(node1 && node2 && node1->next && node2->next) {
            if (node1 == node2) {
                meetNode = node1;
                break;
            }
            node1 = node1->next;
            node2 = node2->next->next;
        }

        if (!meetNode) {
            endA->next = NULL;
            return NULL;
        }
        
        node1 = meetNode->next;
        node2 = headB;
        while(node1 != node2) {
            node1 = node1->next;
            node2 = node2->next;
        }
        
        endA->next = NULL;
        return node1;
    }
};

解法3:参考九章。
思路很清晰。先两个链表各自遍历,如果最后没走到一起就返回NULL。
然后算好两个链表的长度差,短的从头开始走,长的从长度差那个地方开始 走,走到一起的地方就是交汇点。

代码如下:

class Solution {
public:
    /**
     * @param headA: the first list
     * @param headB: the second list
     * @return: a ListNode
     */
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        // write your code here
        if(headA == NULL || headB == NULL)
            return NULL;
        ListNode* iter1 = headA;
        ListNode* iter2 = headB;
        int len1 = 1;
        while(iter1->next != NULL)
        {
            iter1 = iter1->next;
            len1 ++;
        }
        int len2 = 1;
        while(iter2->next != NULL)
        {
            iter2 = iter2->next;
            len2 ++;
        }
        if(iter1 != iter2)
            return NULL;
        if(len1 > len2)
        {
            for(int i = 0; i < len1-len2; i ++)
                headA = headA->next;
        }
        else if(len2 > len1)
        {
            for(int i = 0; i < len2-len1; i ++)
                headB = headB->next;
        }
        while(headA != headB)
        {
            headA = headA->next;
            headB = headB->next;
        }
        return headA;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值