带环链表得几个问题

本文探讨了链表中的三个问题:1) 如何判断链表是否存在环并找到入环点;2) 判断两个链表是否相交及获取交点;3) 对于有环链表,判断它们是否相交并找到相交点。通过慢指针和快指针的方法解决链表环问题,利用链表长度对比解决相交问题。
摘要由CSDN通过智能技术生成

1、判断一个链表是否有环,如果有环,则返回入环的第一个节点,否则返回null

如果一个链表有环,遍历一个链表便永远不会到达null,否则必定会到达null。设置两个指针,刚开始都指向头节点,遍历时,一个指针每次前进一步,我们称之为慢指针,另一个每次前进两步,称之为快指针,这样,如果链表没有环,快指针必然会到达null,如果有环,两个指针必然会相交于一点。


下面再来看看如何找到入环的第一个节点,假设从头节点到入环第一个节点的长度为m,两个指针第一次相遇时的节点距离环入口点的长度为t,环的长度为n,那么此时慢指针走的长度为m+t,快指针走的长度为m+t+kn(k=0,1,2...),kn项表示快指针有可能在环内走了几圈后再与慢指针相遇,这时满足


m+t+kn = 2(m+t)

简化得到

m = (k-1)n + n - t

其中n-t项正表示了慢指针走完当前环还需要走的长度,从这里就可以看出此时如果将快指针放回头节点,然后让它每次只走一步,那么这两个指针必然会相交,并且相交的交点就是入环点。


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

        while ( fast && fast->next )
        {
            slow = slow->next;
            fast = fast->next->next;
            if ( slow == fast ) break;
        }

        if (fast == NULL || fast->next == NULL)
            return NULL;

        slow = head;
        while (slow != fast)
        {
             slow = slow->next;
             fast = fast->next;
        }

        return slow;
}


2、判断两个链表是否相交,相交则返回第一个交点

如果两个链表相交,则交点后面部分两个链表是相同的,当然长度也就相同,关键在于两个链表不相交的部分,所以可以先求出两个链表的长度len1和len2,如果len1大于len2,则链表1先开始往前走,直到从当前节点开始两个链表的长度相同,然后再同时前进,直到两个节点相同,就是第一个交点。


    int getListLength(ListNode *list)
    {
        ListNode *p = list;
        int len = 0;
        while (p)
        {
            len++;
            p = p->next;
        }

        return len;
    }

    /**
     * @param headA: the first list
     * @param headB: the second list
     * @return: a ListNode
     */
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
    {
        if (!headA || !headB)
        {
            return nullptr;
        }
        int lenA = getListLength(headA);
        int lenB = getListLength(headB);

        while (lenA > lenB)
        {
            headA = headA->next;
            lenA--;
        }

        while (lenA < lenB)
        {
            headB = headB->next;
            lenB--;
        }

        while (headA != headB)
        {
            headA = headA->next;
            headB = headB->next;
        }

        return headA;
    }

3、如何判断两个有环链表是否相交,相交则返回第一个相交点

这个问题可以首先利用问题1分别获得两个链表的环入口点loop1和loop2,此时如果loop1和loop2相等,则表明两个链表在入环前就已经相交,这个时候可以利用问题2的方法获得相交点,只是把loop1当作尾节点即可。
如果loop1和loop2不相等呢,这个时候有两种情况,要么两个链表不相交,要么两个链表在环内相交。这个时候可以从loop1出发走一圈,如果在回到loop1的时候一直没有遇到loop2,那么表明两个链表不相交,否则就相交,此时返回loop1或者loop2都可以。


	ListNode* bothLoop(ListNode* head1, ListNode* loop1, ListNode* head2, ListNode* loop2)
	{
		ListNode* cur1 = nullptr;
		ListNode* cur2 = nullptr;

		if (loop1 == loop2)
		{
			cur1 = head1;
			cur2 = head2;
			int n = 0;
			while (cur1 != loop1)
			{
				n++;
				cur1 = cur1->next;
			}
			while (cur2 != loop2)
			{
				n--;
				cur2 = cur2->next;
			}
			cur1 = n > 0 ? head1 : head2;
			cur2 = cur1 == head1 ? head2 : head1;
			n = abs(n);
			while (n != 0)
			{
				n--;
				cur1 = cur1->next;
			}
			while (cur1 != cur2)
			{
				cur1 = cur1->next;
				cur2 = cur2->next;
			}
			return cur1;
		}
		else
		{
			cur1 = loop1->next;
			while (cur1 != loop1)
			{
				if (cur1 == loop2)
				{
					return loop1;
				}
				cur1 = cur1->next;
			}
			return nullptr;
		}
	}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值