C语言实现单链表面试题--进阶(带环问题)

7 篇文章 0 订阅
1.判断单链表是否带环?若带环,求环的长度?求环的入口点?
2.判断两个链表是否相交,若相交,求交点。(假设链表不带环)

3.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】




函数如下:

typedef int DataType;

typedef struct ListNode
{
	DataType data;
	ListNode* next;
}ListNode;

int GetCycleLen(ListNode* plist);				//求环长度(若无环则返回0)
ListNode* IsCycle(ListNode* plist);				//求带环单链表快慢指针相遇点(无环返回NULL)
ListNode* GetCycleEntry(ListNode* plist);			//求带环单链表入口点(无环返回NULL)

ListNode* IsCrossNoCycle(ListNode* plist1, ListNode* plist2);	//判断2个无环链表是否相交
ListNode* IsCross(ListNode* plist1, ListNode* plist2);		//判定2个链表是否相交(分类讨论)


1.判断是否带环

思路:快慢指针,若能相遇则带环

ListNode* IsCycle(ListNode* plist)
{
	ListNode* fast = plist, *slow = plist; //注意这种定义形式
	
	while(fast && fast->next)
	{
		fast = fast->next->next;
		slow = slow->next;
		if(fast == slow)
		{
			return slow;
		}
	}

	return NULL;
}


2.求环的长度

思路:从快慢指针相遇点走一圈,用计数器计数

int GetCycleLen(ListNode* plist)
{
	//先判断是否为带环单链表,不是的话返回0
	ListNode* meet = IsCycle(plist);
	
	if(meet)
	{
		int count = 1;
		ListNode* cur = meet->next;
		while(meet != cur)
		{
			cur = cur->next;
			count++;
		}
		return count;
	}
	
	//若meet为空(没有环),返回0
	else
	{
		return 0;
	}
}


3.判断入口点

思路:设头结点到入口点距离L,入口点到相遇点距离X,环长度C
    因为快指针是慢指针的两倍,且在相遇点相遇,所以 2(L+X) = L+X+C*n(快指针领先圈数)
    解得L=n*C-X
    所以从快慢指针相遇点和头结点一起走,他们的相遇点就是入口点

ListNode* GetCycleEntry(ListNode* plist)
{
	ListNode* meet = IsCycle(plist);
	if(meet)
	{
		while (meet != plist)
		{
			meet = meet->next;
			plist = plist->next;
		}
		return plist;
	}
	else
	{
		return NULL;
	}
}


4.无环链表相交

思路:计算两个链表的长度差的绝对值gab,长的链表先移动gab步,然后两个链表一起走。如果在走到结尾之前相遇了,则该相遇点就是交点

ListNode* IsCrossNoCycle(ListNode* plist1, ListNode* plist2)
{
	//plist1和plist2都是无环链表则进入循环,且plist1和plist2不为空
	if(IsCycle(plist1) == NULL && IsCycle(plist2) == NULL && plist1 && plist2)
	{
		ListNode* cur1 = plist1, *cur2 = plist2;
		
		//求plist1和plist2的长度
		int len1 = 0;
		int len2 = 0;
		while (cur1)
		{
			len1++;
			cur1 = cur1->next;
		}
		while (cur2)
		{
			len2++;
			cur2 = cur2->next;
		}
		
		//移动较长的链表的指针,让他和短的链表一样长
		int gap = abs(len1 - len2);
		if (len1 > len2)
		{
			while(gap--)
			{
				plist1 = plist1->next;
			}
		}
		else
		{
			while(gap--)
			{
				plist2 = plist2->next;
			}
		}
		
		//一起走,如果中途相等了则有交点
		while (plist1)
		{
			if(plist1 == plist2)
			{
				return plist1;
			}
			plist1 = plist1->next;
			plist2 = plist2->next;
		}
	}
	return NULL;
}


5.任意两个链表判断相交:

1) 两个都不带环;

2) 其中一个带环;

3) 两个都带环

ListNode* IsCross(ListNode* plist1, ListNode* plist2)
{
	ListNode* ent1 = GetCycleEntry(plist1);
	ListNode* ent2 = GetCycleEntry(plist2);
	
	//1.两个都不带环,转化成无环相交问题
	if (ent1 == NULL && ent2 == NULL)
	{
		return IsCrossNoCycle(plist1, plist2);
	}

	//2.其中一个带环,必定不相交
	else if ((ent1 == NULL && ent2) || (ent2 == NULL && ent1))
	{
		return NULL;
	}

	//3.两个都带环
	//	1)不相交
	//	2)尾交
	//	3)环交
	
	else
	{
		//1)若入口点相同则尾交,去掉环,转化成无环相交问题
		if(ent1 == ent2)
		{
			ent1->next = NULL;
			return IsCrossNoCycle(plist1, plist2);
		}

		else
		{
			//2)同环,两个入口点
			//  解决方案:一个入口点开始遍历一圈,看环上是否有另一个入口点
			//  若找到则返回plist1的入口点
			ListNode* cur = ent1->next;
			while (cur != ent1)
			{
				if(cur == ent2)
				{
					return ent1;
				}
				cur = cur->next;
			}
			return NULL;	//不相交
		}
	}
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值