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

来源:互联网 发布:网络的拓扑结构 编辑:程序博客网 时间:2024/06/06 05:33
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){//先判断是否为带环单链表,不是的话返回0ListNode* meet = IsCycle(plist);if(meet){int count = 1;ListNode* cur = meet->next;while(meet != cur){cur = cur->next;count++;}return count;}//若meet为空(没有环),返回0else{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;//不相交}}}