【CareerCup】 Linked Lists—Q2.5

来源:互联网 发布:37轩辕剑神器进阶数据 编辑:程序博客网 时间:2024/05/22 07:43

转载请注明出处: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;elsereturn 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

11 1