链表有环相关问题
来源:互联网 发布:戴墨镜叼烟修图软件 编辑:程序博客网 时间:2024/06/15 18:01
1、如何判断一个链表是否有环?
2、如果有环的话,如何计算环的长度?
3、如何找到环的入口?
4、找到两条链表的第一个交点
1、现在先来解决第一个问题:
可以设置两个指针fast和slow都指向链表头,fast指针每次向前走两步,slow指针每次向前走一步。
如果fast指针遇到NULL了,说明该链表没有环。
如果链表有环,fasr指针每次都比slow指针走的快,最终两个指针会相遇。
bool IsCircle(ListNode *head){ //链表为空或链表只有结点就不用判断了 if(head==NULL || head->m_pNext==NULL) return false; ListNode *slow = head; ListNode *fast = head; while(fast!=NULL && fast->m_pNext!=NULL) { fast = fast->m_pNext->m_pNext; slow = slow->m_pNext; if(fast == slow) { break; } } /* 跳出循环有两种情况: 1、要么是fast指针遇上NULL跳出循环,此时没环 2、有环,fast指针和slow指针相遇了 */ if(fast==NULL || fast->m_pNext==NULL) { return false;//第一种情况 } else { return true;//第二种情况 }}ListNode *Search(ListNode *head,int key){ for(ListNode *p=head;p!=NULL;p=p->m_pNext) { if(p->m_nKey == key) return p; } return NULL;}int main(){ //先测试没有环的链表 ListNode *head = NULL; for(int i=0;i<10;i++) { InsertTail(&head,i); } Show(head); bool flag = IsCircle(head); if(!flag) cout<<"没有环"<<endl; else cout<<"有环"<<endl; //测试有环的链表 //构造环 ListNode *p = Search(head,2); ListNode *q = Search(head,9); q->m_pNext = p; flag = IsCircle(head); if(!flag) cout<<"没有环"; else cout<<"有环"; return 0;}
2、关于环的长度
从第一次相遇时开始计数,到第二次相遇时结束计数
int GetCircleLength(ListNode *head){ ListNode *fast = head; ListNode *slow = head; int count = 0;//环的长度 int time = 0;//第几次相遇 while(1) { fast = fast->m_pNext->m_pNext; slow = slow->m_pNext; if(fast == slow) { time++; } if(time == 1)//开始计数 { count++; } if(time == 2)//第二次相遇,结束计数 return count; } return -1;}int main(){ ListNode *head = NULL; for(int i=0;i<10;i++) { InsertTail(&head,i); } //构造环 ListNode *p = Search(head,2); ListNode *q = Search(head,9); q->m_pNext = p; cout<<"环的长度:"<<GetCircleLength(head)<<endl; return 0;}
3、环的入口点
碰撞点p到连接点的距离=头指针到连接点的距离,因此,分别从碰撞点、头指针开始走,相遇的那个点就是连接点。
(1)当fast与slow相遇时,show肯定没有走完链表,而fast已经在还里走了n(n>= 1)圈。假设slow走了s步,那么fast走了2s步。fast的步数还等于s走的加上环里转的n圈,所以有:
2s = s + nr。因此,s = nr。
(2)设整个链表长为L,入口据相遇点X,起点到入口的距离为a。因为slow指针并没有走完一圈,所以:
a + x = s,带入第一步的结果,有:a + x = nr = (n-1)r + r = (n-1)r + L - a;即:
a = (n-1)r + L -a -x;
这说明:从头结点到入口的距离,等于转了(n-1)圈以后,相遇点到入口的距离。因此,我们可以在链表头、相遇点各设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。
ListNode * findCircleEntrance(ListNode *head){ ListNode *fast = head; ListNode *slow = head; while(fast!=NULL && fast->m_pNext!=NULL) { fast = fast->m_pNext->m_pNext; slow = slow->m_pNext; if(fast == slow) { break; } } if(fast==NULL || fast->m_pNext==NULL) return NULL;//说明没环 slow = head; while(slow != fast) { slow = slow->m_pNext; fast = fast->m_pNext; } return slow;}int main(){ ListNode *head = NULL; for(int i=0;i<10;i++) { InsertTail(&head,i); } //构造环 ListNode *p = Search(head,2); ListNode *q = Search(head,9); q->m_pNext = p; ListNode *s = findCircleEntrance(head); if(s != NULL) cout<<"环的入口:"<<s->m_nKey<<endl; return 0;}
///////////////////////////////
4、找到两条链表的第一个交点?????????????
(1)首先要判断两条链表是否有环,如果一条链表有环,一条链表没环,那肯定就没有交点了
(2)如果链表有环,断开环(断开环之前要保存下一个结点的值,因为之后还要还原)
(3)现在两条链表都没有环,求出两个链表的长度。
(4)将两条链表相差的长度先走完,两个链表再一起走,相遇的第一点即为第一个交点
(5)还原环,判断是否有交点
int GetLength(ListNode *head)//得到链表长度{ int count = 0; for(ListNode *p=head;p!=NULL;p=p->m_pNext) count++; return count;}ListNode *FirstIntersection(ListNode *head1, ListNode *head2){ //判断head1和head2是否有环 ListNode *p = findCircleEntrance(head1); ListNode *q = findCircleEntrance(head2); //一个有环另一个没环则一定不相交 if( (p==NULL && q!=NULL) || (p!=NULL && q==NULL)) return NULL; //用于保存断开环之前入口的下一个结点 ListNode *pnext = NULL; ListNode *qnext = NULL; if(p != NULL)//p有环则将环断开,之后需要还原 { pnext = p->m_pNext; p->m_pNext = NULL; } if(q != NULL)//q有环则将环断开,之后需要还原 { qnext = q->m_pNext; q->m_pNext = NULL; } //两个链表都没有环 int len1 = GetLength(head1); int len2 = GetLength(head2); ListNode *r = head1->m_pNext; ListNode *s = head2->m_pNext; /*将head1和head2的多余的长度先走完,两个链表再一起走, 相遇的第一点即为第一个交点*/ while(len1 > len2) { r = r->m_pNext; len1--; } while(len2 > len1) { s = s->m_pNext; len2--; } //多余的长度走完了,两个链表一起走,查找交点 while(r!=NULL && s!=NULL) { if(r == s) { break; } r = r->m_pNext; s = s->m_pNext; } //将断开的环还原 if(pnext != NULL) { p->m_pNext = pnext; } if(qnext != NULL) { q->m_pNext = qnext; } //判断是否有交点 if(r==s && r!=NULL) return r; else return NULL;}int main(){ //1 先测试两条不相交的链表(没有环) ListNode *head1 = NULL; ListNode *head2 = NULL; for(int i=0;i<10;i++)//构造两条不相交的链表 { InsertTail(&head1,i); InsertTail(&head2,i+20); } Show(head1); Show(head2); cout<<"两条无环不相交的链表:"; ListNode *p = FirstIntersection(head1,head2); if(p == NULL) cout<<"没有交点"<<endl; else cout<<"第一个交点为"<<p->m_nKey<<endl; cout<<endl<<endl; //2 构造环 测试一个有环的链表和一个没环的链表 ListNode *r = Search(head1,2); ListNode *s = Search(head1,9); s->m_pNext = r; cout<<"一个有环的链表和一个没环的链表:"; p = FirstIntersection(head1,head2); if(p == NULL) cout<<"没有交点"<<endl; else cout<<"第一个交点为"<<p->m_nKey<<endl; //看环是否还原 bool flag = IsCircle(head1); if(!flag) cout<<"没有环"<<endl; else cout<<"有环"<<endl; cout<<endl<<endl; //3 测试两条没有环,有交点的链表 ListNode *head3 = NULL; ListNode *head4 = NULL; for(int i=0;i<10;i++)//构造两条不相交的链表 { InsertTail(&head3,i); } InsertTail(&head4,20); InsertTail(&head4,21); p = Search(head3,3); ListNode * q = Search(head4,20); assert(p!=NULL && q!=NULL && q->m_pNext!=NULL); q->m_pNext = p;//3为两条链的第一个交点 cout<<"两条没有环,有交点的链表:"; p = FirstIntersection(head3,head4); if(p == NULL) cout<<"没有交点"<<endl; else cout<<"第一个交点为"<<p->m_nKey<<endl; Show(head3); Show(head4); cout<<endl<<endl; //构造两个个相交的且有环的链表 p = Search(head3,5); q = Search(head3,9); assert(p!=NULL && q!=NULL && q->m_pNext==NULL); q->m_pNext = p; //构造环 cout<<"两条有环,有交点的链:"; p = FirstIntersection(head3,head4); if(p == NULL) cout<<"没有交点"<<endl; else cout<<"第一个交点为"<<p->m_nKey<<endl; return 0;}
- 链表有环相关问题
- 问题相关
- 相关问题,
- 问题相关
- i++相关问题
- 域名DNS相关问题
- SendMessage相关问题
- 网络通讯相关问题
- tomcat 相关的问题
- linux 的相关问题
- Java相关编码问题
- sizeof 的相关问题
- TESTLINK中文相关问题
- PageBase类相关问题
- 相关打印问题
- WAP WML 相关问题
- 软件许可证相关问题
- RFC3261相关学习[问题]
- 数据结构之顺序栈
- 5.并发工具和执行器(Concurrency Utilities and Executors)5.1 并发工具介绍
- 从这里开始
- java并发之同步屏障CyclicBarrier
- JSP中四种传递参数的方法
- 链表有环相关问题
- 4种解析方式的方式
- Android开发--更换字体
- shell和C语言第一课
- mybatis里面的模糊查询
- Xshell连接VM中的linux系统
- Android 中 Activity的跳转
- Windows任务栏遮挡
- 2017.10.12 第三天总结