判断两个链表是否相交,若相交,求交点(假设链表不带环)
- 遍历两个链表到最后一个结点,若两链表的最后一个结点地址相同则相交
- 求得两个指针的长度,然后长链表先遍历(长链表-短链表)步,然后两链表同时向后遍历,当链表遍历到结点地址相同的时候,该结点为交点
pNode FindInterNode(pNode p1, pNode p2)
{
pNode cur1 = p1;
pNode cur2 = p2;
int len1 = 0;
int len2 = 0;
int front = 0;
while (cur1->next != NULL)
{
cur1 = cur1->next;
}
while (cur2->next != NULL)
{
cur2 = cur2->next;
}
if (cur1 == cur2)//条件成立则相交
{
cur1 = p1;
cur2 = p2;
len1 = GetListLength(cur1);
len2 = GetListLength(cur2);
if (len1 > len2)
{
front = len1 - len2;
while (front--)
{
cur1 = cur1->next;
}
}
else
{
front = len2 - len1;
while (front--)
{
cur2 = cur2->next;
}
}
while (cur1 != cur2)
{
cur1 = cur1->next;
cur2 = cur2->next;
}
return cur1;
}
else
{
return NULL;
}
}
判断单链表是否带环?若带环,求环的长度?求环的入口点
- 借助快慢指针:当慢指针进入到环中,若快指针追上快指针(快慢指针相交)则说明带环,若快指针往后走的过程中遇到空指针域,则说明不带环
//如果有环,返回两指针在环内的相遇结点,无环返回空,后面的题目会用到此结点位置
pNode IsCircleLinkList(pNode plist)
{
pNode fast = plist;//快指针一次走一步
pNode low = plist;//慢指针一次走两步
while (fast != NULL)
{
low = low->next;
if (fast->next == NULL)
{
break;
}
fast = fast->next;
if (fast->next == NULL)
{
break;
}
fast = fast->next;
if (fast == low)
{
return low;
}
}
return NULL;
}
- 快慢指针相交时,再遍历一次链表,当回到交点位置,所遍历的长度即是环的长度
int LengthOfCircle(pNode plist)
{
int count = 0;
pNode cur = plist;
pNode NodeInCircle = NULL;
NodeInCircle = IsCircleLinkList(plist);
//从头指针遍历到相遇结点位置
while (cur != NodeInCircle)
{
cur = cur->next;
}
//计数时判断结点指针域,循环结束时count = 环的长度 - 1
while (cur->next != NodeInCircle)
{
count++;
cur = cur->next;
}
count++;
return count;
}
- (1)从结点处断开链表(将结点的next指针域指向空),然后将此问题转换为求两相交链表交点的问题。这种方法会将该链表破坏,如果后面还要使用该链表则会带来问题
pNode EntranceOfCircle(pNode plist)
{
pNode ret = NULL;
pNode cur1 = plist;
pNode cur2 = NULL;
pNode NodeInCircle = NULL;
NodeInCircle = IsCircleLinkList(plist);
cur2 = NodeInCircle->next;
NodeInCircle->next = NULL;//找到环内相遇结点,断开环
ret = FindInterNode(cur1, cur2);
return ret;
}
- (2)两个指针同时向后遍历,一个指针从环内交点开始,一个指针从头开始,最后肯定会在环的入口点相遇
pNode EntranceOfCircle_OP(pNode plist)
{
pNode cur1 = plist;
pNode cur2 = IsCircleLinkList(plist);
while (cur1 != cur2)
{
cur1 = cur1->next;
cur2 = cur2->next;
}
return cur1;
}
复制复杂链表
通过一般链表的复制方法,将新旧链表分开,无法复制出新链表的random指针指向,因为如果再通过一次同步循环遍历,会将新链表的random指针指向老链表random指针所指向的结点
新旧链表串成一条链表,此时便可以通过老链表的random找到新链表的random,复制完成后,将两条链表分开
构造复杂链表
typedef struct CNode
{
DataType data;
struct CNode *next;
struct CNode *random;
}CNode, *pCNode;
pCNode BuyCNode(DataType data)
{
pCNode newNode = (CNode *)malloc(sizeof(CNode));
assert(newNode);
newNode->data = data;
newNode->next = NULL;
newNode->random = NULL;
return newNode;
}
void CNTest()
{
pCNode plist = NULL;
pCNode ret = NULL;
pCNode plist1 = BuyCNode(1);
pCNode plist2 = BuyCNode(2);
pCNode plist3 = BuyCNode(3);
pCNode plist4 = BuyCNode(4);
plist = plist1;
plist1->next = plist2;
plist2->next = plist3;
plist3->next = plist4;
plist1->random = plist3;
plist2->random = plist1;
plist3->random = plist3;
ret = CopyComplexLinkList(plist);
}
pCNode CopyComplexLinkList(pCNode plist)
{
pCNode ret = NULL;
pCNode cur = plist;
pCNode newNode = NULL;
pCNode next = NULL;
pCNode newNext = NULL;
//新旧链表串一起
while (cur != NULL)
{
newNode = BuyCNode(cur->data);
newNode->next = cur->next;
cur->next = newNode;
cur = newNode->next;
}
//然后复制random
cur = plist;
while (cur != NULL)
{
newNode = cur->next;
if (cur->random != NULL)
{
newNode->random = cur->random->next;
}
cur = cur->next->next;
}
//新旧链表分开来
cur = plist;
ret = plist->next;//记录新链表起始位置
while (cur != NULL)
{
newNode = cur->next;
next = newNode->next;
if (next == NULL)
{
newNext = NULL;
}
else
{
newNext = next->next;
}
cur->next = next;
newNode->next = newNext;
cur = next;
}
//最后返回新链表
return ret;
}
- (1)新旧链表串一起
- (2)然后复制random
- (3)新旧链表分开来
- (4)最后返回新链表
判断两个链表是否相交,若相交,求交点(链表可能带环)
根据上面的分析,两链表出现的状态一共有下面6种情况,画图表示