判断链表相交
编写一个程序,找到两个单链表相交的起始节点。
如下面的链表:
在节点 c1 开始相交。
注意:
如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
对,这里单链表是不存在"X"型相交的因为单链表的节点只能指向一个地址,所以只有"Y"型和"V"型
所以可以先分别求出两个链表的长度,然后求出它们的长度差将长的那个链表进行访问,直到剩余长度与短链表一样时,开始同时访问并判断各自节点的地址是否相同,第一个地址相同的节点就是我们要找的交点,如果直到NULL都没有找到,说明这两个链表并不相交:
具体实现:
int getlongth(struct ListNode *target){
int count =0;
while(target!=NULL){
count++;
target = target->next;
}
return count;
}
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode* LongHead;
struct ListNode* shortHead;
int lenA = getlongth(headA);
int lenB = getlongth(headB);
int cha = 0;
if(lenA>=lenB){
cha = lenA-lenB;
LongHead = headA;
shortHead = headB;
}
else{
cha = lenB-lenA;
LongHead = headB;
shortHead = headA;
}
for(int i = 0;i<cha;i++){
LongHead = LongHead->next;
}
while(LongHead!=shortHead){
shortHead = shortHead->next;
LongHead = LongHead->next;
if(!LongHead){
return NULL;
}
}
return LongHead;
}
判断链表是否带环(要求时间复杂度为O(1))
示例 :
链表中有一个环,其尾部连接到第二个节点。
这里我们要明白,带环链表是没有尽头的,它会一直死循环下去,我们假设它是一条奇怪的格子赛道,将两个指针比作运动员A和运动员B,A的脚程一次一格,B的是一次两格,当比赛开始,B一马当先,按理说他应该会先到达终点,但这个赛道是没有尽头的,他将在环里一直跑下去,过了一会,A也来了,他们将在环里相遇,据此我们可以设置快慢两个指针,当开始后快的那一个不是先等于NULL而是和慢指针的地址相同时,就可以确定是环形列表了
具体实现:
bool hasCycle(struct ListNode *head) {
struct ListNode *fast = head;
struct ListNode *slow = head;
if (!head){
return false;
}
while(1){
fast = fast->next;
if(fast==NULL){
return false;
}
slow = slow->next;
fast = fast->next;
if(fast==NULL){
return false;
}
if(fast==slow){
return true;
}
}
}
这里你可能会奇怪,fast比slow走的快就行了,可以是其他的步数吗?当然可以,但注意,某些组合在特殊场景下是它们之永远无法相遇的:假设快指针一次三格,慢指针一次一格,当这个环形赛道只有两格时,他们将永远不在一格上
求链表带环时环的入口节点
不可以修改链表
按照上面的算法,我们似乎只能找到快慢两指针相遇的位置,这个位置当然不会一定在入口处,那麽我们该如何确定呢?
我们假设链表从头节点到入口的距离是x,从入口到相遇点的距离是y,而链表的长度是d,当快慢指针相遇时:
慢指针 | 快指针 | |
---|---|---|
路程 | x+y+n*d | 2(x+y+n*d) |
也就是> | x+y+(k*d) |
我们将快指针的两个式子等立得到:x=h*d-y;(h=k-2n)从头结点到环入口的距离等于从相遇点到环入口再加上h-1倍的环长度;所以我们可以说当两个指针分别从从相遇点和头节点出发,那么它们必然会在入口点相遇:
具体实现:
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *fast = head;
struct ListNode *slow = head;
if (!head){
return NULL;
}
while(1){
fast = fast->next;
if(fast==NULL){
return NULL;
}
slow = slow->next;
fast = fast->next;
if(fast==NULL){
return NULL;
}
if(fast==slow){
break;
}
}
struct ListNode *n1 = head;
struct ListNode *n2 = slow;
while(n1!=n2){
n1 = n1->next;
n2 = n2->next;
}
return n2;
}
求环长度
有了前面的基础,这里实现起来非常简单,我们可以任意选择环入口节点或相遇节点,用一个指针记录当前位置,再使用另一个指针进行遍历同时记录步数,这两个指针相等时,步数就是环的长度:
具体实现
int GetCyclLongth(struct ListNode *pow)
{
int count = 1;
for(struct ListNode*go = pow;go->next!=pow;go =go->next,count++)
{;}
return count;
}