转载请注明出处:http://blog.youkuaiyun.com/ns_code/article/details/22097191
题目:
Given a circular linked list, implement an algorithm which returns node at the beginning of the loop.
DEFINITION
Circular linked list: A (corrupt) linked list in which a node’s next pointer points to an earlier node, so as to make a loop in the linked list.
EXAMPLE
Input: A -> B -> C -> D -> E -> C [the same C as earlier]
Output: C
翻译:
给定一个循环链表,实现一个算法返回这个环的开始结点。
定义:
循环链表:链表中一个结点的指针指向先前已经出现过的某个结点,导致链表中出现环。
示例:
输入:A -> B -> C -> D -> E -> C [结点C在之前已经出现过]
输出:结点C
思路:最直观且容易理解的方法(借鉴编程之美上的思路)是:
设两个指针p1和p2,一个移动速度快(每次移动两个节点的距离),一个移动速度慢(每次移动一个节点的距离),刚开始均指向表头,如果链表中存在环,则快指针最终会追上慢指针。二者遇的节点肯定是环中的一个节点,此时我们固定其中一个指针p1,而后以每次一个节点的速度移动p2,记录下p2最终又回到p1所在节点位置时走过的节点数M,M即为环中节点的个数。而后我们再将两个指针同时指向表头,先固定p1,让p2以每次一个节点的速度移动M个节点,而后让p1和p2同时移动,这样当p1刚到环入口时,p2刚好转了一圈回到环入口,因此二者相遇的节点便是环开始的节点。
Hawstein的blog中给出了另外两种方法,一种也是设置上面两个指针来分析的,貌似有点繁琐,没仔细看。另一种用哈希表,这个我也想到了,但是感觉没法恰当地契合,而且用地址做哈希表的键值,对内存貌似有很大的浪费哦,所以感觉这个不算是个很好的方法!另外,对C++也不熟啊!哎。。。搞java的人伤不起啊!
我还想了另外一种方法,在链表中多加一个域(这种方法可能很离谱,就好像给你个问题去解决,你把问题的参数给改了,然后再去解决它,但我觉得可以作为解决一些问题的思路,其实依然有哈希的思想在里面),如下:
typedef struct Node
{
ElemType data;
bool flag;
struct Node *next;
}
建链表时,flag全部置为false,而后从头遍历时,每遍历一个节点,如果发现其flag为false,则将其置为true,如果发现其为true,则说明该节点已经遍历过了,那第一次出现访问到flag为ture的节点便是环开始的节点。
实现代码:
我们这里采用第一种方法,我们在程序中构建好如下循环链表:
完整代码如下:
/*********************************
题目描述:
求一个有环的单链表中环开始处的节点
Date:2014-03-26
**********************************/
typedef int ElemType;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node,*pNode;
#include<stdio.h>
#include<stdlib.h>
/*
定义一个速度为2的快指针,一个速度为1的慢指针,
如果链表中有环,则返回两个指针相遇的节点,
如果没有链表中没有环,则返回NULL。
*/
pNode WetherCircle(pNode pHead)
{
if(!pHead)
return NULL;
pNode p1 = pHead;
pNode p2 = pHead;
//直到二者相遇,退出循环
while((p1 && p2 && p1!=p2) || (p1==p2 && p1==pHead && p2==pHead))
{
p1 = p1->next;
p2 = p2->next->next;
}
if(p1 == p2)
return p1;
else
return NULL;
}
/*
计算环中的节点个数
*/
int CircleLen(pNode pHead)
{
pNode p = WetherCircle(pHead);
if(!p)
return 0;
int count = 1;
//固定一个指针,另一个指针在环中移动
pNode p1 = p->next;
while(p1 != p)
{
count++;
p1 = p1->next;
}
return count;
}
/*
求环开始的节点
*/
pNode CircleBegin(pNode pHead)
{
int len = CircleLen(pHead);
if(len < 1)
return NULL;
pNode p1 = pHead;
pNode p2 = pHead;
int i;
//第一个指针先移动len个节点
for(i=0;i<len;i++)
p1 = p1->next;
//而后一起移动
while(p1 != p2)
{
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
/*
建立如下所示的带环的单链表
1->2->3->4->5->6->7->4
即环的入口节点为date域为4的节点
*/
pNode create_CircleList()
{
pNode pHead = (pNode)malloc(sizeof(Node));
if(!pHead)
{
printf("malloc faild!\n");
exit(-1);
}
pHead->data = 1;
pNode r = pHead;
int i;
for(i=0;i<6;i++)
{
pNode pNew = (pNode)malloc(sizeof(Node));
if(!pNew)
{
printf("malloc faild!\n");
exit(-1);
}
pNew->data = i + 2;
r->next = pNew;
r = pNew;
}
//将最后一节点的next指向第四个节点,形成环
r->next = pHead->next->next->next;
return pHead;
}
int main()
{
pNode pHead = create_CircleList();
pNode p = CircleBegin(pHead);
printf("The date in the beginNode of the Circle is:%d\n",p->data);
return 0;
}
测试结果:
注:代码开源到我的Github:https://github.com/mmc-maodun/CareerCup