【CareerCup】 Linked Lists—Q2.5

转载请注明出处: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的节点便是环开始的节点。

    实现代码:

    我们这里采用第一种方法,我们在程序中构建好如下循环链表:

【CareerCup】 Linked Lists—Q2.5_第1张图片

    完整代码如下:

/*********************************
题目描述:
求一个有环的单链表中环开始处的节点
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

你可能感兴趣的:(LinkedList,Careercup)