一, 判断单链表是否带环?若带环,求环的长度?求环的入口点?
(1)判断是否带环:利用快慢指针法,快指针一次走两步,慢指针一次走一步,若快慢指针相遇,说明链表带环,并且返回相遇点即此时慢指针所指处,无环则返回NULL。
//判断是否带环
Node* IsHaveLoop(Node* head)
{
if(head == NULL)
{
return NULL;
}
Node* fast = head;
Node* slow = head;
while(fast->next && fast)
{
fast=fast->next->next;
slow=slow->next;
if(fast=slow)
{
return slow;
}
}
return NULL;
}
(2)求环长:从相遇点出发,并开始计数,遍历一遍后再次回到相遇点,累加之数即为环地长度。
//求环长
int Lenloop(Node* head)
{
Node* meet = IsHaveLoop(head);
if(meet)
{
int count = 1;
Node* tmp = meet->next;
while(tmp!=meet)
{
tmp = tmp->next;
count++;
}
return count;
}
return 0;
}
(3)求环的入口点:一个指针从链表头出发,一个指针从相遇点出发,两指针相遇处即为入口点。(这里可能不太好理解,后面我会图示举例讲解)
//求入口点
Node Enter(Node* head)
{
Node* meet = IsHaveLoop(head);
if(meet)
{
while(meet!=head)
{
meet = meet->next;
head= head->next;
}
return head;
}
return NULL;
}
借助一个带环链表的实例:
二,初级版:判断两个链表是否相交,若相交,求交点。(假设链表不带环)
这种情况比较简单,首先遍历链表求得长度,计算长链表和短链表的差值Diflen,第二次遍历让较长的链表先走差值步,接着同时在两个链表上遍历,找到的第一个相同的节点就是两个链表的交点。
//求链表长度
int Getlen(Node* head)
{
if(head == NULL)
{
return 0;
}
int count = 0;
Node* tmp = head;
while(tmp->next)
{
count++;
tmp = tmp->next;
}
return count;
}
//判断链表是否相交并求交点
Node* IsInter1(Node* head1, Node* head2)
{
int len1 = Getlen(head1);
int len2 = Getlen(head2);
int Diflen = len1-len2;
Node* long = head1;
Node* short = head2;
if(len1<len2)
{
long = head2;
short = head1;
}
for(int i = 0;i<Diflen;++i)
{
long = long->next;//快指针先走相差的步数
}
while(long)
{
long = long->next;//快慢指针一起走
short = short->next;
if(long == short)
{
return long;
}
}
return NULL;
}
三,升级版:判断两个链表是否相交,若相交,求交点。(假设链表可能带环)
这种情况比较复杂,分类进行分析:
Node* IsInter2(Node* head1,Node* head2)
{
if((head1=NULL)||(head2=NULL)
{
return NULL;
}
Node* enter1 = Enter(head1);
Node* enter2 = Enter(head2);
//1情景:两个链表都不带环,转换为上面问题求解
if((enter1 == NULL)&&(enter2 == NULL)
{
return IsInter1(head1,head2);
}
//2情景:一个带环一个不带环且不相交
else if((enter1 == NULL)&&(enter2 != NULL)||(enter1 != NULL)&&(enter2 == NULL)
{
return NULL;
}
//3情景:在环的入口点前面相交,去掉环转换成无环问题
else if(enter1 == enter2)
{
enter1->next = NULL;//去掉环
enter2->next = NULL;
return IsInter1(head1,head2);
}
//4情景:同环,两个入口点
else
{
Node* tmp = enter1->next;
while(tmp != enter)
{
if(tmp = enter2)
{
return enter1;
}
tmp = tmp->next;
}
return NULL;
}
}
四,复杂链表的复制
实现ComplexListNode*Clone(ComplexListNode* head),复制一个复杂链表。在复杂链表中,每个节点除了有一个next指针指向下一个节点,还有一个sibling指针指向链表中的任意节点。
原始链表如图:
节点定义:
struct ComplexListNode
{
int data;
ComplexListNode* next;
ComplexListNode* sibling;
};
第一步:复制原始链表的任意节点X并创建*X,再把每一个*X连接到对应的X后面。
void CloneNode(ComplexListNode* head)//复制链表节点
{
if(head == NULL)
{
return NULL;
}
ComplexListNode* cur = head;
ComplexListNode* cloned;
while(cur != NULL)
{
cloned = new ComplexListNode(cur->data);
cur->next = cloned;
cloned->next = cur->next;
cur = cloned->next;
}
}
第二步:设置复制出来的节点的sibling。假设原始链表上的X的sibling指向节点Y,复制节点对应的sibling指向节点Y的next节点*Y。
void Clonesibling(ComplexListNode* head)//设置复制节点的sibling
{
ComplexListNode* cur = head;
ComplexListNode* cloned;
while(cur!=NULL)
{
cloned = cur->next;
if(cur->sibling != NULL)
{
cloned->sibling = cur->sibling->next;
}
cur = cloned ->next;
}
}
第三步:把长链表拆分成两个链表。奇数位置的节点链接起来就是原始链表,偶数位置的节点链接起来就是复制出来的链表。
ComplexListNode* ReconNode(ComplexListNode* head)
{
ComplexListNode* cur = head;
ComplexListNode* clonedhead = NULL;
ComplexListNode* clonedcur = NULL;
if(cur != NULL)//连接奇数节点也就是原始节点
{
clonedhead = clonedcur = cur->next;
cur->next = clonedcur -> next;
cur = cur -> next;
}
while(cur!=NULL)
{
clonedcur->next = cur->next;//连接偶数节点也就是复制节点
clonedcur = clonedcur->next;
cur->next = clonedcur->next;
cur = cur->next;
}
return clonedhead;
}
最后把上面三步整合起来就是复制复杂链表的过程。
ComplexListNode* Clone(ComplexListNode* head)
{
CloneNode(head);
Clonesibling(head);
return ReconNode(head);
}