一、题目
1、判断单链表是否带环?若带环,求环的长度?求环的入口点?
①链表是否带环问题。( 定义两个指针同时指向头节点,一个快指针,一个慢指针,两个指针同时遍历链表,快指针一次走两步,慢指针一次走一步,如果链表带环,两个指针一定会在环内相遇。例如两个人在操场同时跑步,跑得快的人一定会追上跑得慢的人。)
②在第一问中已经可以求得两指针相遇点,在定义一个指针,从相遇点出发开始遍历环,在此走到相遇点,便是一圈的长度,即环长。
③环的入口点见下图。
2、判断两个链表是否相交,若相交,求交点。(假设链表不带环)
问题思路看下图
3、判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】
4、求两个已排序单链表中相同的数据。
二、代码及测试结果
1、判断单链表是否带环?若带环,求环的长度?求环的入口点?
SListNode *CircleSListNode(SListNode *list) //时间复杂度为O(n)
{
SListNode *slow,*fast;
assert(list);
slow=fast=list;
while (fast && fast->_next) //快指针走两步,慢指针走一步,若带环,则一定在环内相遇
{
slow=slow->_next;
fast=fast->_next->_next;
if (fast == slow)
{
return slow; //返回相遇点地址
}}
return NULL;
}
size_t CircleLengthSListNode(SListNode *ps) //求环长,时间复杂度为O(n)
{
SListNode *tmp;
size_t i=0;
assert(ps);
tmp =ps;
do //从环的相遇点开始走,走一圈便是环长
{
tmp=tmp->_next;
++i;
} while (tmp!=ps);
return i;
}
SListNode *CircleEntrySListNode(SListNode *list,SListNode *meet) //时间复杂度为O(1)
{
SListNode *start;
assert(list);
start=list;
while (meet != start)
{
meet=meet->_next;
start=start->_next;
}
return meet;
}
测试代码及结果:
void Test13()
{
// 判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每个算法的时间复杂度&空间复杂度。
SListNode *entry,*end;
printf("原链表:");
SListPrint(SList);
entry=SListFind(SList,1);
end=SListFind(SList,18);
end->_next=entry;
printf("\n快慢指针相遇点地址:");
printf("%p\n",CircleSListNode(SList));
printf("\n环长:");
printf("%d\n",CircleLengthSListNode(CircleSListNode(SList))); //环长
printf("\n环入口地址:");
printf("%p\n",CircleEntrySListNode(SList,CircleSListNode(SList))); //环入口地址
printf("\n环入口地址处数据:");
printf("%d\n",CircleEntrySListNode(SList,CircleSListNode(SList))->_data); //环入口地址
}
2、判断两个链表是否相交,若相交,求交点。(假设链表不带环)
代码:
int IsCross(SListNode* list1, SListNode* list2) //判断是否相交,若尾节点地址相等则相交
{
while (list1 && list1->_next)
{
list1 = list1->_next;
}
while (list2 && list2->_next)
{
list2 = list2->_next;
}
if (list1 == list2 && list1 != NULL)
{
return 1;
}
else
{
return 0;
}
}
SListNode *SListCrossNode(SListNode *list1,SListNode *list2) //交点地址
{
SListNode *tmp1,*tmp2;
DataType k=0;
assert(list1 && list2);
tmp1=list1;
tmp2=list2;
if(1 != (IsCross(list1,list2)))
return NULL;
while (tmp1->_next) //遍历两个链表,求出链表的长度
{
tmp1=tmp1->_next;
k++;
}
while (tmp2->_next)
{
tmp2=tmp2->_next;
k--;
}
tmp1=list1;
tmp2=list2;
if (k>0) //让两个链表走到长度相等处
{
while (k--)
{
tmp1=tmp1->_next;
}
}
else
{
k=abs(k);
while (k--)
{
tmp2=tmp2->_next;
}
}
while (tmp1!=tmp2) //两个链表同时从相等处开始走,走到有地址相等且不等于空则相交
{
tmp1=tmp1->_next;
tmp2=tmp2->_next;
}
return tmp1;
}
测试代码及结果:
void Test14()
{
SListNode *cross,*end,*entry;
SListPushFront(&list1,5); //头插
SListPushFront(&list1,15);
SListPushBack(&list1,10); //尾插
SListPushBack(&list1,12);
SListPushBack(&list1,11);
SListPushFront(&list1,25);
SListPushFront(&list1,35);
SListPushBack(&list1,28);
SListPushFront(&list1,25);
SListPushFront(&list1,35);
printf("链表1:");
SListPrint(list1);
SListPushBack(&list2,444); //尾插
SListPushBack(&list2,56);
SListPushBack(&list2,0);
SListPushBack(&list2,15);
SListPushBack(&list2,25);
SListPushFront(&list2,18);
SListPushFront(&list2,35);
printf("链表2:");
SListPrint(list2);
cross=SListFind(list2,25);
cross->_next=SListFind(list1,10);
printf("\n交点:");
printf("%p\n",SListCrossNode(list1,list2)); //两链表是否相交
printf("%d\n",SListCrossNode(list1,list2)->_data);
3、判断两个链表是否相交,若相交,求交点。(假设链表可能带环)。
SListNode* SListIsCross(SListNode *list1,SListNode *list2)
{
SListNode *meet1,*meet2;;
assert(list1&&list2);
meet1=CircleSListNode(list1); //判断 两个链表是否带环
meet2=CircleSListNode(list2);
if (meet1 && meet2 ) //两个链表带环
{
SListNode *tmp=meet1->_next;
while (tmp!=meet1) //判断是否带同一个环
{
if (tmp==meet2) //带同一个环
{
SListNode *cur1,*cur2;
cur1=CircleEntrySListNode(list1,meet1);
cur2=CircleEntrySListNode(list2,meet2);
if (cur1==cur2) //环外相交
{
SListNode *tmp1,*tmp2;
tmp1=list1;
tmp2=list2;
while (tmp1 != cur1)
{
tmp1=tmp1->_next;
}
tmp1->_next=NULL;
while (tmp2 != cur2)
{
tmp2=tmp2->_next;
}
tmp2->_next=NULL;
return SListCrossNode(list1,list2); //返回交点地址
}
else //环内相交
{
return cur1; //返回交点地址
}
}
tmp=tmp->_next;
}
return NULL; //带两个不同的环
}
else if (NULL==meet1 && NULL==meet2) //两个链表都不带环
{
return SListCrossNode(list1,list2);
}
else //可能一个带环
return NULL;
}
测试代码及结果:
SListNode *cross,*end,*entry;
SListPushFront(&list1,5); //头插
SListPushFront(&list1,15);
SListPushBack(&list1,10); //尾插
SListPushBack(&list1,12);
SListPushBack(&list1,11);
SListPushFront(&list1,25);
SListPushFront(&list1,35);
SListPushBack(&list1,28);
SListPushFront(&list1,25);
SListPushFront(&list1,35);
printf("链表1:");
SListPrint(list1);
SListPushBack(&list2,444); //尾插
SListPushBack(&list2,56);
SListPushBack(&list2,0);
SListPushBack(&list2,15);
SListPushBack(&list2,25);
SListPushFront(&list2,18);
SListPushFront(&list2,35);
printf("链表2:");
SListPrint(list2);
cross=SListFind(list2,25);
cross->_next=SListFind(list1,10);
entry=SListFind(list1,5);
end=SListFind(list1,28);
end->_next=entry;
printf("\n交点:");
printf("%p\n",SListIsCross(list1,list2)); //判断两链表相交(可能带环)
printf("\n%d\n",SListIsCross(list1,list2)->_data);
}
4、求两个已排序单链表中相同的数据
void SListUnionData(SListNode *list1,SListNode *list2)
{
SListNode *tmp1,*tmp2;
assert(list1 && list2);
tmp1=list1;
tmp2=list2;
while (tmp1 && tmp2)
{
if (tmp1->_data<tmp2->_data) //小的指针向前走一步
{
tmp1=tmp1->_next;
}
else if(tmp1->_data>tmp2->_data)
{
tmp2=tmp2->_next;
}
else //数据相等时同时走
{
printf("%d ",tmp2->_data);
tmp1=tmp1->_next;
tmp2=tmp2->_next;
}
}
printf("\n");
}
测试代码及结果:
void Test15()
{
SListPushFront(&list1,5); //头插
SListPushFront(&list1,15);
SListPushBack(&list1,10); //尾插
SListPushBack(&list1,12);
SListPushBack(&list1,11);
SListPushFront(&list1,25);
SListPushFront(&list1,3);
SListPushBack(&list1,28);
SListPushFront(&list1,2);
SListPushFront(&list1,35);
SeqListSort(list1); //冒泡排序
printf("链表1:");
SListPrint(list1);
SListPushBack(&list2,4); //尾插
SListPushBack(&list2,56);
SListPushBack(&list2,0);
SListPushBack(&list2,15);
SListPushBack(&list2,25);
SListPushFront(&list2,18);
SListPushFront(&list2,35);
SeqListSort(list2);
printf("链表2:");
SListPrint(list2);
printf("相同的数据:");
SListUnionData(list1,list2);//求两个已排序单链表中相同的数据。
}