C語言實現單鏈表面試題--進階(帶環問題)
阿新 • • 發佈:2019-01-03
1.判斷單鏈表是否帶環?若帶環,求環的長度?求環的入口點?
2.判斷兩個連結串列是否相交,若相交,求交點。(假設連結串列不帶環)
3.判斷入口點
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; //不相交
}
}
}