#include <iostream>
#include <stdio.h>
using namespace std;
/*
问题:给定一个有环链表,实现一个算法返回环路的开头节点
分析:快慢指针,一个每次走一步,一个每次走两步,如果能够相遇,必定存在环
现在关键是如何找到环路初始节点
未解决:关键是要寻找相遇点y,环起始点x,环的长度L 三者之间的关系,目前关系忘了
书上分析:设头结点距离环的起始点距离为L,设从环起始点走了长度k处相遇,设环的总长度为m
则第一次相遇时:
快指针走的距离S1=L + m + k (1)
慢指针走的距离S2=L + k (2)
满足: S1=2*S2 (3)
解方程得L = m - k
该式子的意思是: 头结点距离环的起始点距离=相遇点距离环起始点的距离
推得规律:
1 头指针和相遇点处指针同时每次走一步,那么相遇的地方就是环的起始点
2 环的长度=快慢指针在相遇点出继续走再次相遇时所走的步数
输入:
5(链表长度) 2(环的起始下标)
0 1 2 3 4(链表中每个节点的元素)
输出:
又换
换的其实地址
环的长度3
关键:
1 设头结点距离环的起始点距离为L,设从环起始点走了长度k处相遇,设环的总长度为m
则第一次相遇时:
快指针走的距离S1=L + m + k (1)
慢指针走的距离S2=L + k (2)
满足: S1=2*S2 (3)
解方程得L = m - k
该式子的意思是: 头结点距离环的起始点距离=相遇点距离环起始点的距离
推得规律:
1 头指针和相遇点处指针同时每次走一步,那么相遇的地方就是环的起始点
2 环的长度=快慢指针在相遇点出继续走再次相遇时所走的步数
2 环的起始点=头结点,相遇节点分别每次走一步后,相遇之后的节点
3 环的长度=快慢指针在相遇节点处再次相遇所走的步数
*/
typedef struct Node
{
int value;
Node* pNext;
}Node;
//构建连边,按照尾插法来做,返回节点值到出现次数的映射,k表示环形链表的值
void buildList(int* pArray , Node* head , int num , int k)
{
if(pArray == NULL)
{
return;
}
if(head == NULL)
{
return;
}
//尾插法: 保留最后一个结尾节点,将新生成的节点插入在结尾节点,并令结尾节点为当前节点
Node* pLast = head;
Node* pCyclicNode = NULL;
for(int i = 0 ; i < num ; i++)
{
int value = *(pArray + i);
Node* pNode = new Node();
pNode->value = value;
pLast->pNext = pNode;
pLast = pNode;
if(i == k)
{
pCyclicNode = pNode;
}
}
//找到环形节点后,就令最后一个节点指向环形节点
pLast->pNext = pCyclicNode;
}
//如果有环,则返回相遇的节点
Node* isHasCycle(Node* pHead)
{
if(pHead == NULL)
{
return false;
}
Node* pFast = pHead->pNext;
Node* pSlow = pFast;
while( pSlow && pFast)
{
pFast = pFast->pNext->pNext;
pSlow = pSlow->pNext;
//相遇
if(pSlow == pFast)
{
break;
}
}
//如果没有环,pFast应该已经为空了
if(pFast == NULL)
{
return NULL;
}
return pFast;
}
//寻找环形链表的起始环节点,提供相遇的节点,根据 头结点距离环形节点的距离k=相遇节点距离环形节点的距离,则从头结点和相遇节点分别继续向下走,相遇处即为所求
Node* findCyclicBeginNode(Node* pHead , Node* meetNode)
{
Node* tempNode = meetNode;
if(pHead == NULL || meetNode == NULL)
{
return NULL;
}
Node* pNode = pHead->pNext;
while(pNode != meetNode)
{
pNode = pNode->pNext;
meetNode = meetNode->pNext;
}
meetNode = tempNode;
return pNode;
}
//计算环形链表中环的长度 = 快慢指针在相遇处继续按照快慢步走后,下次相遇时所走的步数
int calculateCycleLength(Node* pHead , Node* meetNode)
{
if(pHead == NULL || meetNode == NULL)
{
return -1;
}
Node* pFast = meetNode->pNext->pNext;
Node* pSlow = meetNode->pNext;
int count = 1;
while(pFast != pSlow)
{
pFast = pFast->pNext->pNext;
pSlow = pSlow->pNext;
count++;
}
return count;
}
void printList(Node* pHead)
{
if(NULL == pHead)
{
return;
}
Node* pNode = pHead->pNext;
while(pNode)
{
cout << pNode->value << " ";
pNode = pNode->pNext;
}
cout << endl;
}
//注意去除环形链表中环形节点
void releaseList(Node* pHead)
{
if(NULL == pHead)
{
return;
}
Node* pNode = pHead->pNext;
Node* pPrevious = pHead;
while(pNode)
{
Node* pDeleteNode = pNode;
pPrevious->pNext = pNode->pNext;
pNode = pNode->pNext;
pPrevious = pPrevious->pNext;
delete pDeleteNode;
}
//删除头结点
delete pHead;
}
int main(int argc, char* argv[])
{
int n , k;
while(cin >> n >> k)
{
int* pArr = new int[n];
for(int i = 0 ; i < n ; i++)
{
cin >> pArr[i];
}
Node* pHead = new Node();
buildList(pArr , pHead , n , k);
Node* meetNode = isHasCycle(pHead);
Node* beginNode = findCyclicBeginNode(pHead , meetNode);
int length = calculateCycleLength(pHead , meetNode);
if(meetNode != NULL)
{
cout << "链表存在环" << endl;
cout << "环的起始节点值为:" << beginNode->value << endl;
cout << "环的长度为:" << length << endl;
}
else
{
cout << "链表不存在环" << endl;
}
//printList(pHead);
releaseList(pHead);
delete[] pArr;
}
system("pause");
return 0;
}
程序员面试金典: 9.2链表 2.6给定有环链表,实现算法返回环路的开头节点
最新推荐文章于 2021-10-09 15:37:22 发布