转载请注明出处:http://blog.csdn.net/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