判断两个链表是否相交,若相交返回其交点位置(两链表都不带环)
解析:两个不带环的链表要能相交只能是Y字型相交,如下图。
所以判断相交只需要分别让两个链表都走到结束时,判断他们的data值是否相等。
//实现
LinkNode* HasCross(LinkNode* head1, LinkNode* head2)//判断两个链表是否相交
{
if(head1 == NULL)
{
return NULL;
}
if(head2 == NULL)
{
return NULL;
}
LinkNode* cur1 = head1;
LinkNode* cur2 = head2;
size_t len1 = 0;
size_t len2 = 0;
while(cur1->Next != NULL)
{
cur1 = cur1->Next;
len1++;
}
while(cur2->Next != NULL)
{
cur2 = cur2->Next;
len2++;
}
if(cur1->data != cur2->data)
{
return NULL;
}
size_t len = 0;
cur1 = head1;
cur2 = head2;
if(len1 > len2)
{
len = len1-len2;
while(len--)
{
cur1 = cur1->Next;
}
while(cur1->data != cur2->data)
{
cur1 = cur1->Next;
cur2 = cur2->Next;
}
return cur1;
}
else
{
len = len2-len1;
while(len--)
{
cur2 = cur2->Next;
}
while(cur1->data != cur2->data)
{
cur1 = cur1->Next;
cur2 = cur2->Next;
}
return cur2;
}
}
判断两个链表是否相交,可能带环
两个链表总共有三种情况,两个都不带环、都带环、一个带环一个不带环,那就要分开讨论。如果都不带环就是我们刚才所写的Y字相交法,如果一个带环一个不带环那么是不可能相交的。着重讨论一下两个都带环的。
由此可见,若两个链表都带环且相交将有三种情况需要讨论,逐步进行。
//实现
LinkNode* HasCross2(LinkNode* head1, LinkNode* head2)//判断两个链表是否相交,可能带环
{
if(head1 == NULL)
{
return NULL;
}
if(head2 == NULL)
{
return NULL;
}
LinkNode* ret1 = GetCycleEntry(head1);//调用之前的函数查看是否带环
LinkNode* ret2 = GetCycleEntry(head2);
if(ret1 == NULL && ret2 == NULL)
{
HasCross(head1,head2);//两个都不带环
}
else if((ret1 == NULL && ret2 != NULL) || (ret2 == NULL && ret1 != NULL))//一个不带环一个带环不可能相交
{
return NULL;
}
else
{
if(ret1->data == ret2->data)
{
//交点在环外
size_t len1 = 0;
size_t len2 = 0;
LinkNode* tmp1 = head1;
LinkNode* tmp2 = head2;
while(tmp1 != ret1)
{
len1++;
tmp1 = tmp1->Next;
}
while(tmp2 != ret1)
{
len2++;
tmp2 = tmp2->Next;
}
size_t len = 0;
tmp1 = head1;
tmp2 = head2;
if(len1 > len2)
{
len = len1-len2;
while(len--)
{
tmp1 = tmp1->Next;
}
while(tmp1->data != tmp2->data)
{
tmp1 = tmp1->Next;
tmp2 = tmp2->Next;
}
return tmp1;
}
else
{
len = len2-len1;
while(len--)
{
tmp2 = tmp2->Next;
}
while(tmp1->data != tmp2->data)
{
tmp1 = tmp1->Next;
tmp2 = tmp2->Next;
}
return tmp2;
}
}
else//交点在环内
{
LinkNode* cur1 = ret1;
while(cur1 != ret2)
{
if(cur1->Next == ret1)
{
return NULL;//遍历一圈发现没有相同的,那么就没有相交
}
cur1 = cur1->Next;
}
return ret2;
}
}
}
求两个已排序链表中的相同数据
两个已排序链表当中,如果其中一个链表遍历结束,那么另外一个就没有遍历的必要了,因为必定没有相同的元素,将两个链表相同的元素插入一个新的链表即可。
//实现
LinkNode* UnionSet(LinkNode* head1, LinkNode* head2)//求两个已排序链表中的相同数据
{
if(head1 == NULL)
{
return NULL;
}
if(head2 == NULL)
{
return NULL;
}
LinkNode* new_head = NULL;
LinkNode* tail = NULL;
LinkNode* cur1 = head1;
LinkNode* cur2 = head2;
while(cur1 != NULL && cur2 != NULL)
{
if(cur1->data < cur2->data)
{
cur1 = cur1->Next;
}
else if(cur1->data > cur2->data)
{
cur2 = cur2->Next;
}
else
{
if(new_head == NULL)
{
new_head = Creat(cur1->data);
tail = new_head;
}
else
{
tail->Next = Creat(cur1->data);
tail = tail->Next;
}
cur1 = cur1->Next;
cur2 = cur2->Next;
}
}
return new_head;
}
这个较为简单,所以不再赘述。
复杂链表的复制问题
首先说说什么是复杂链表,复杂链表就是链表的每个节点内的元素多一个random指针,这个指针可以指向任意位置,可以指向链表内其他元素,也可以指向NULL,所以在对其进行复制的时候需要注意对random的复制。
对复杂链表的复制这里有两个思路可以进行。
首先先定义一个新的节点
#define Comtype char//节点内元素类型
typedef struct ComplexNode{
Comtype data;
struct ComplexNode* next;
struct ComplexNode* random;
}ComplexNode;//定义一个新类型的复杂单链表节点
接着,再封装一些基本函数来确保后面操作的执行。
//新节点的创建
ComplexNode* Creat1(Comtype value)//创建一个新的节点
{
ComplexNode* new_Node = (ComplexNode*)malloc(sizeof(ComplexNode));
if( new_Node == NULL)
{
printf("申请失败\n");
return NULL;
}
new_Node->data = value;
new_Node->next = NULL;
new_Node->random = NULL;
return new_Node;
}
//对复杂链表的打印,即要打印data,还有打印random所指向的内容
void ComPrint(ComplexNode* head)
{
if(head == NULL)
{
return;
}
ComplexNode* cur = head;
for( ; cur!=NULL; cur = cur->next)
{
printf("[%c] ",cur->data);
}
printf("\n");
cur = head;
for( ; cur!=NULL; cur = cur->next)
{
if(cur->random == NULL)
{
printf("[%c]->%p ",cur->data,cur->random);
continue;
}
printf("[%c]->[%c] ",cur->data,cur->random->data);
}
printf("\n");
}
思路一
将链表内的每个元素的random的指向利用其偏移量的方式进行保存,偏移量是random指向的元素相对于头指针的偏移距离。如下图。
然后再进行复制新链表的data内容,复制完毕以后根据偏移量在进行random的复制。
//实现
void OffSet(ComplexNode* head, ComplexNode* new_head, ComplexNode* pos, ComplexNode* tail)//计算偏移量,并且根据复制其偏移量指向
{
if(head == NULL)
{
return;
}
if(pos == NULL)
{
return;
}
if(pos->random == NULL)
{
tail->random = NULL;
return;
}
ComplexNode* cur = head;
ComplexNode* tmp = new_head;
while(cur->data != pos->random->data)
{
cur = cur->next;
tmp = tmp->next;
}
tail->random = tmp;
}
ComplexNode* CopyComplex(ComplexNode* head) //复杂链表的复制
{
if(head == NULL)
{
return NULL;
}
ComplexNode* new_head = NULL;
ComplexNode* tail = NULL;
ComplexNode* cur = head;
while(cur != NULL)
{
if(new_head == NULL)
{
new_head = Creat1(cur->data);
tail = new_head;
}
else
{
tail->next = Creat1(cur->data);
tail = tail->next;
}
cur = cur->next;
}
cur = head;
tail = new_head;
while(cur != NULL)
{
OffSet(head, new_head, cur, tail);
cur = cur->next;
tail = tail->next;
}
return new_head;
}
思路二
利用重复插入的方法,将每一个节点的后面再次插入一个与其相同的节点,且random的指向也是相同的,最后再将重复的拆下即可。如下图。
//实现
void ComPush(ComplexNode* head, ComplexNode* cur, Comtype value)//在每个元素后插入一个相同的元素
{
if(head == NULL)
{
return;
}
ComplexNode* tmp = cur->next;
cur->next = Creat1(value);
cur->next->next = tmp;
}
ComplexNode* CopyComplex2(ComplexNode* head) //复杂链表的复制
{
if(head == NULL)
{
return NULL;
}
ComplexNode* cur = head;
while(cur != NULL)
{
ComPush(head,cur,cur->data);
cur = cur->next->next;
}
cur = head;
ComplexNode* tmp = cur->next;
while(1)
{
if(cur->random == NULL)
{
cur = cur->next->next;
if(cur == NULL)
{
break;
}
tmp = cur->next;
continue;
}
tmp->random = cur->random->next;
cur = cur->next->next;
if(cur == NULL)
{
break;
}
tmp = cur->next;
}
cur = head;
tmp = cur->next;
ComplexNode* new_head = NULL;
ComplexNode* tail = NULL;
while(1)
{
if(new_head == NULL)
{
new_head = tmp;
cur->next = tmp->next;
tail = new_head;
}
else
{
tail->next = tmp;
cur->next = tmp->next;
tail = tail->next;
}
cur = cur->next;
if(cur == NULL)
{
break;
}
tmp = cur->next;
}
return new_head;
}
测试代码如下:
void TestCom()
{
HEAD;
printf("\n");
printf("\n");
printf("\n");
ComplexNode* head;
ComplexNode* a = Creat1('a');
ComplexNode* b = Creat1('b');
ComplexNode* c = Creat1('c');
ComplexNode* d = Creat1('d');
head = a;
a->next = b;
b->next = c;
c->next = d;
b->random = d;
c->random = a;
a->random = d;
d->random = NULL;
printf("=======================原链表=========================\n");
ComPrint(head);
ComplexNode* new_head = CopyComplex(head);
printf("=======================新链表=========================\n");
ComPrint(new_head);
}
void TestCom2()
{
HEAD;
printf("\n");
printf("\n");
printf("\n");
ComplexNode* head;
ComplexNode* a = Creat1('a');
ComplexNode* b = Creat1('b');
ComplexNode* c = Creat1('c');
ComplexNode* d = Creat1('d');
head = a;
a->next = b;
b->next = c;
c->next = d;
b->random = d;
c->random = a;
a->random = d;
d->random = NULL;
printf("=======================原链表=========================\n");
ComPrint(head);
printf("=======================新链表=========================\n");
CopyComplex2(head);
ComPrint(head);
}
void Test1()
{
TestCom();
TestCom2();
}
欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!