您的位置:首页 > 职场人生

程序员面试金典: 9.2链表 2.6给定有环链表,实现算法返回环路的开头节点

2016-12-21 17:00 323 查看
#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
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐