在实现单链表的基本与基础的操作后:
我们可以在单链表中考虑环与相交以及复杂单链表的问题。
1.判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每个算法的时间复杂度&空间复杂度:
首先判断是否有环,可以定义快慢指针,fast走俩步,slow走一步,如果是带环链表fast最后一定会在环内与slow相遇,无环的fast会先到头。
因为fast比slow走的快,所以进入环是fast一定在slow前面(或相遇),假设领先n步,fast每次必slow先走一步,所以每走一次俩者间距离减少为n-1步,最后俩者一定会相遇。相遇时返回相遇点。
最后我写了一个制作环的函数,用以测试以后的代码。
ListNode* IsCycle(ListNode *pList)//是否带环
{
if ((pList == NULL) || (pList->next == NULL))
return NULL;
ListNode *fast = pList->next->next;
ListNode *slow = pList->next;
while ((fast != slow) && fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
}
if (fast == slow)
return fast;
else
return NULL;
}
//pos在链表内
void GetCycle(ListNode **ppList, ListNode *pos)//做带环链表
{
assert(ppList && *ppList);
ListNode *tail = *ppList;
while (tail->next)
{
tail = tail->next;
}
tail->next = pos;
}
首先为了求环的长度,我们想到只要让某个指针在环内走一圈(不停走,再次遇到自己)时,所走的步数就是环的长度。我们不知道尾巴的长度无法判断一个指针何时进入环,所以可以利用上面的快慢指针,求出快慢指针的相遇点,然后走一圈即可。因为此情况必定带环,所以不用考虑控的情况。
为求环的入口点我们对环进行推论。fast在与slow相遇时比他多走了N圈,最后得出结论L=NC-X 所以让俩个指针分别从相遇点与头节点开始走,最后一定会在入口处相遇。
//该链表带环
ListNode* CycldeMeNode(ListNode *pList)//求环的相遇点
{
ListNode *fast = pList->next->next;
ListNode *slow = pList->next;
while (fast != slow)
{
fast = fast->next->next;
slow = slow->next;
}
return fast;
}
int GetCycleLen(ListNode *meetNode)//求环长度
{
ListNode *cur = meetNode->next;
int count = 1;
while (cur != meetNode)
{
cur = cur->next;
count++;
}
return count;
}
ListNode* GetCycleEntry(ListNode* pList, ListNode *meetNode)//求环入口点
{
ListNode* cur = pList;
ListNode* ent = meetNode;
while (ent != cur)
{
ent = ent->next;
cur = cur->next;
}
return ent;
}
用以测试上面代码的主函数,注意空链表等特殊条件。
void TestList()
{
ListNode *list = NULL;
Pushback(&list, 1);
Pushback(&list, 2);
Pushback(&list, 3);
Pushback(&list, 4);
Pushback(&list, 5);
PrintList(list);
ListNode *pos = Find(list, 2);
GetCycle(&list, pos);
ListNode *meetNode = IsCycle(list);
if (meetNode)
printf("***带环***\n");
else
printf("***不带***\n");
printf("***meetNode: %d\n", meetNode->data);
int len = GetCycleLen(meetNode);
printf("***Len: %d\n", len);
ListNode *ent = GetCycleEntry(list, meetNode);
printf("***Enter: %d\n", ent->data);
//DestoryList(&list);
}
int main()
{
TestList();
return 0;
}
2.判断两个链表是否相交,若相交,求交点。(假设链表不带环)
首先分析相交情况,只有一种 >— 其余都为他的特殊情况。
1判断俩链表是否相交,同时找到俩链表的尾节点看他们是否指向同一块空间。
2找交点,算出俩链表的长度差gap,然后让长的链表指针向前走gap步,另一链表指针指向头,俩指针同时开始走,当指向同一块空间时就为交点。
3如果俩链表中,有为空即没有交点。
void GetCress(ListNode **ppList, ListNode *pos)//做相交链表
{
assert(ppList);
if (*ppList == NULL)
return;
ListNode *tail = *ppList;
while (tail->next)
{
tail = tail->next;
}
tail->next = pos;
}
ListNode* CressNoc(ListNode* pList1, ListNode* pList2)//无环链表是否相交
{
if ((pList1 == NULL) && (pList2 == NULL))
return NULL;
ListNode *tail1 = pList1;
ListNode *tail2 = pList2;
int count1 = 1;
int count2 = 1;
while (tail1->next)
{
tail1 = tail1->next;
count1++;
}
while (tail2->next)
{
tail2 = tail2->next;
count2++;
}
if (tail1 != tail2)
return NULL;
int gap = abs(count1 - count2);
ListNode *cur1 = pList1;
ListNode *cur2 = pList2;
if (count1 > count2)
{
while (gap--)
{
cur1 = cur1->next;
}
}
else
{
while (gap--)
{
cur2 = cur2->next;
}
}
while (cur1 != cur2)
{
cur1 = cur1->next;
cur2 = cur2->next;
}
return cur1;
}
3.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】
图为可能带环所有的状况,将状况条理化分析并逐步解决。
1.俩者都无环,判断同无环函数(无环链表判断相交函数)
2.其中一个有环, 必不相交
3.俩个都有环,交点在尾部。可类似无环链表的判断,把环的入口点看作相交链表的尾部,判断同无环函数
4.俩个都有环,交点为环。定义快慢指针,分别在俩入口点走,一圈之内必相交。
5.俩个都有环,不相交。在4的条件下不相交。
ListNode* CressMc(ListNode* pList1, ListNode* pList2)//可能带环链表是否相交
{
if ((pList1 == NULL) && (pList2 == NULL))
return NULL;
ListNode* c1 = IsCycle(pList1);
ListNode* c2 = IsCycle(pList2);
//1.俩个都不带环
if ((c1 == NULL) && (c2 == NULL))
{
return CressNoc(pList1, pList2);
}
//2.只有一个带环 必不相交
else if (c1 == NULL || c2 == NULL)
return NULL;
//俩都带环
else
{
ListNode* ent1 = GetCycleEntry(pList1, c1);
ListNode* ent2 = GetCycleEntry(pList1, c2);
//3.俩都带环 交点在尾巴
if (ent1 == ent2)
{
ListNode *tail1 = pList1;
ListNode *tail2 = pList2;
int count1 = 1;
int count2 = 1;
while (tail1->next != ent1)
{
tail1 = tail1->next;
count1++;
}
while (tail2->next != ent2)
{
tail2 = tail2->next;
count2++;
}
int gap = abs(count1 - count2);
ListNode *cur1 = pList1;
ListNode *cur2 = pList2;
if (count1 > count2)
{
while (gap--)
{
cur1 = cur1->next;
}
}
else
{
while (gap--)
{
cur2 = cur2->next;
}
}
while (cur1 != cur2)
{
cur1 = cur1->next;
cur2 = cur2->next;
}
return cur1;
}
//4.俩个都带环 交点为环
else
{
ListNode *fast = ent1->next->next;
ListNode *slow = ent2->next;
int count = GetCycleLen(pList1);
while ((fast != slow) && (--count))
{
fast = fast->next->next;
slow = slow->next;
}
if (fast == slow)
return ent1;
else
return NULL;
}
}
}