给定一个单链表的头指针h,解决以下问题:
- 判断该链表是否有环
- 如果有环,如何知道环的长度
- 找出环的连接点
- 求得单链表的长度
背景
假设单链表有环,则相遇点必定在环上。设,环的长度为r,h为头指针,a为“甩尾”的长度,t为连接点,m1为slow和fast相遇的节点,b为m1距离t的长度(逆时针方向),此时假设slow共走了x步,则fast走了2x步。
问题一
思路
给定两个指针slow和fast,slow一次走一步,fast一次走两步。如果fast能等于slow,则说明有环。
证明
假设单链表有环,且slow走了x = nr步 (n为某个正整数且nr >= a),此时fast和slow走的步数差nr步,且二者都进入环内,则二者必定相遇。换言之,若单链表有环,必定存在一个n使得二者相遇(且不为空)。若没有环,则不存在这一特性。
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == NULL)return false;
ListNode * slow = head;
ListNode * fast = head;
while(fast != NULL && fast->next != NULL){
slow = slow->next;
fast = fast->next->next;
if(slow == fast)return true;
}
return false;
}
};
问题二
思路
如果单链表有环,则从相遇时,固定fast,走slow,开始计步length。当slow再次与fast相遇时,即走了一圈,此时length即为环的长度
代码
int getRingLength(ListNode* head){
ListNode* slow(head),*fast(head);
while(fast != NULL && fast->next != NULL){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
slow = slow->next;
int length = 1;
while(slow != fast){
slow = slow->next;
length++;
}
return length;
}
}
return 0;
}
问题三
思路
如果单链表有环,则找出连接点t。有个定理:分别从头指针和相遇点出发,一次一步,当二者相遇时的节点即为连接点。
证明
有环单链表共有两种情况。
情况一:环中没有节点。即从最后一个节点出发又回到最后一个节点,则b = 1,此时fast一直会在最后一个节点循环,定理成立。
情况二:环中有节点(图1的情况)。假设共走了x步第一次相遇,slow走了x = a + b,fast走了2x = a + nr + b,则a = nr - b。因此,分别从头指针和相遇点出发,头指针走了a步到t,m1则走了nr-b步,刚好也走到了t,定理成立。
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head == NULL)return NULL;
ListNode *slow(head),*fast(head);
while(fast != NULL && fast->next != NULL){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
ListNode* meet = slow;
while(head != meet){
head = head->next;
meet = meet->next;
}
return meet;
}
}
return NULL;
}
};
问题四
思路
链表的长度即等于 “甩尾”长度 + 环长度,即为从头指针到连接点t长度a + 从t出发再次回到t长度
代码
int getListLength(ListNode* head){
if(NULL == head) return 0;
ListNode* meetNode = getCircleStart(head);
int extraLength = 0;
while(meetNode != head){
head = head->next;
extraLength++;
}
int ringLength = getRingLength(head);
return extraLength + ringLength;
}
完整代码
/******
** 有环单链表
** 1.createLoopList 创建“环”,这是一个循环链表
** 2.createLinkList 创建单链表,需要先创建“环”
** 3.showLinkList 打印有环单链表
** 4.judgeRing 判断单链表是否有环
** 5.getRingLength 获得环长
** 6.getCircleStart 获得连接点位置
** 7.getListLength 获得链表总长度
*******/
#include <iostream>
#include <algorithm>
#include <stack>
#include <set>
using namespace std;
typedef struct ListNode{
int value;
ListNode * next;
ListNode(int val):value(val){};
}ListNode,*ListNodePointer;
ListNode * createLoopList(){
int value,n;
cout<<"1.创建循环链表,并返回循环链表起始点。请输入环长度"<<endl;
cin>>n;
cout<<"请输入"<<n<<"个节点值"<<endl;
cin>>value;
ListNode * circleHead = new ListNode(value);
if(NULL == circleHead){
cout<<"创建循环链表失败,节点value:"<<value<<endl;
exit(-1);
}
ListNode * temp = circleHead;
temp->next = circleHead;
for(int i = 1;i < n;++i){
cin>>value;
ListNode * node = new ListNode(value);
if(NULL == node){
cout<<"创建循环链表失败,节点value:"<<value<<endl;
exit(-1);
}
temp->next = node;
node->next = circleHead;
temp = node;
}
return circleHead;
}
ListNode * createLinkList(){
cout<<"创建单链表,需要1.先创建循环链表 2.创建甩尾"<<endl;
ListNode * startCircle = createLoopList();
ListNode * temp(startCircle),*head(NULL);
cout<<"2.倒着输入甩尾部分节点"<<endl;
int value;
while(cin>>value){
ListNode* node = new ListNode(value);
node->next = temp;
temp = node;
}
head = temp;
return head;
}
void showLinkList(ListNode* head){
if(NULL == head)
return;
cout<<"打印单链表"<<endl;
set<ListNode*> nodeSet;
while(head != NULL && nodeSet.count(head) == 0){
nodeSet.insert(head);
cout<<head->value<<" ";
head = head->next;
}
cout<<endl;
}
bool judgeRing(ListNode* head){
ListNode* slow(head),*fast(head);
while(fast != NULL && fast->next != NULL){
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
return true;
}
return false;
}
int getRingLength(ListNode* head){
ListNode* slow(head),*fast(head);
while(fast != NULL && fast->next != NULL){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
slow = slow->next;
int length = 1;
while(slow != fast){
slow = slow->next;
length++;
}
return length;
}
}
return 0;
}
ListNode * getCircleStart(ListNode* head){
ListNode* slow(head),*fast(head);
while(fast != NULL && fast->next != NULL){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
ListNode* meet = slow;
while(head != meet){
head = head->next;
meet = meet->next;
}
return meet;
}
}
return NULL;
}
int getListLength(ListNode* head){
if(NULL == head) return 0;
ListNode* meetNode = getCircleStart(head);
int extraLength = 0;
while(meetNode != head){
head = head->next;
extraLength++;
}
int ringLength = getRingLength(head);
return extraLength + ringLength;
}
int main(int argc,char ** argv){
ListNode * head = createLinkList();
showLinkList(head);
cout<<"链表"<<(judgeRing(head)?"含有":"不含有")<<"环,环长度为"<<getRingLength(head);
ListNode * meetNode = getCircleStart(head);
cout<<"。环连接点值为"<<meetNode->value<<",链表总长度为"<<getListLength(head)<<endl;
return 0;
}