菜鸟系列之C/C++经典试题(七)

找含单链表的环入口点

问题1:如何判断单链表中是否存在环(即下图中从结点E到结点R组成的环)?

分析:设一快一慢两个指针(Node *fast, *low)同时从链表起点开始遍历,其中快指针每次移动长度为2,慢指针则为1。则若无环,开始遍历之后fast不可能与low重合,且fast或fast->next最终必然到达NULL;若有环,则fast必然不迟于low先进入环,且由于fast移动步长为2,low移动步长为1,则在low进入环后继续绕环遍历一周之前fast必然能与low重合(且必然是第一次重合)。于是函数可写如下:

bool hasCircle(Node* head, Node* &encounter)
{
	Node *fast = head, *slow = head;
	while(fast && fast->next)
	{
		fast = fast->next->next;
		slow = slow->next;
		if(fast == slow)
		{
			encounter = fast;
			return true;
		}
	}
	encounter = NULL;
	return false;
}

问题2:若存在环,如何找到环的入口点(即上图中的结点E)?

解答:如图中所示,设链起点到环入口点间的距离为x,环入口点到问题1中fast与low重合点的距离为y,又设在fast与low重合时fast已绕环n周(n>0),且此时low移动总长度为s,则fast移动总长度为2s,环的长度为r。则

s + nr = 2s,n>0       ①

s = x + y              ②

由①式得  s = nr

代入②式得

nr = x + y

x = nr - y               ③

现让一指针p1从链表起点处开始遍历,指针p2从encounter处开始遍历,且p1和p2移动步长均为1。则当p1移动x步即到达环的入口点,由③式可知,此时p2也已移动x步即nr
- y步。由于p2是从encounter处开始移动,故p2移动nr步是移回到了encounter处,再退y步则是到了环的入口点。也即,当p1移动x步第一次到达环的入口点时,p2也恰好到达了该入口点。于是函数可写如下:

Node* findEntry(Node* head, Node* encounter)
{
	Node *p1 = head, *p2 = encounter;
	while(p1 != p2)
	{
		p1 = p1->next;
		p2 = p2->next;
	}
	return p1;
}

原文来自:http://blog.csdn.net/wuzhekai1985/article/details/6725263

有错误欢迎提出, 分享请标明出处, 谢谢!

感觉好的话就顶一个, 感觉不错的话就踩一个。

时间: 2024-10-16 17:09:40

菜鸟系列之C/C++经典试题(七)的相关文章

菜鸟系列之C/C++经典试题(三)

设计包含min函数的栈 题目:定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素.要求函数min.push以及pop的时间复杂度都是O(1). 分析:这是2006年google的一道面试题. 我看到这道题目时,第一反应就是每次push一个新元素时,将栈里所有逆序元素排序.这样栈顶元素将是最小元素.但由于不能保证最后push进栈的元素最先出栈,这种思路设计的数据结构已经不是一个栈了. 在栈里添加一个成员变量存放最小元素(或最小元素的位置).每次push一个新元素进栈的时候,如果该元素比

菜鸟系列之C/C++经典试题(一)

这个我写菜鸟系列之C/C++ 的第一篇文章 题目:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表.要求不能创建任何新的结点,只调整指针的指向. 比如将二元查找树 10 /    \ 6       14 /  \     / \ 4     8  12   16 转换成双向链表 4=6=8=10=12=14=16. 分析:本题是微软的面试题.很多与树相关的题目都是用递归的思路来解决,本题也不例外.下面我们用两种不同的递归思路来分析. 思路一:当我们到达某一结点准备调整以该结点为根结点

菜鸟系列之C/C++经典试题(二)

求子数组的最大和 题目描述: 输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.要求时间复杂度为O(n). 例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4,7, 2,因此输出为该子数组的和18. 这个问题在各大公司面试中出现频率之频繁,被人引用次数之多,非一般面试题可与之匹敌.ok,下面,咱们来一步一步分析这个题. 分析与解法 解法一 求一个数组的最大子数组和

菜鸟系列之C/C++经典试题(四)

题目一:查找最小的k个元素 输入n个整数,输出其中最小的k个. 例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4. 分析:这道题最简单的思路莫过于把输入的n个整数排序,这样排在最前面的k个数就是最小的k个数.只是这种思路的时间复杂度为O(nlogn).我们试着寻找更快的解决思路. 我们可以先创建一个大小为k的数据容器来存储最小的k个数字.接下来我们每次从输入的n个整数中读入一个数.如果容器中已有的数字少于k个,则直接把这次读入的整数放入容器之中:如果容器中已有k

菜鸟系列之C/C++经典试题(十)

打印1到最大的n位数 题目:输入数字n,按顺序打印出从1到最大的n位十进制数.比如输入3,则打印1, 2, 3,-,999. 方法一: 这道题一看感觉很简单,首先求的n位数的最大值,然一个从1到这个最大值的循环就搞定了, 如果真的就把这样的答案面试官的话, 后果很是严重.首先, 没有考虑到获得的这个最大的数会不会溢出,如果溢出了, 答案肯定不对.这个代码比较简单, 我就不列出来了. 方法二: 很明显, 这道题我们可以用递归做, 就是每当多添加一位时, 我就把所以的都打印一遍连带刚添加的这个位,

菜鸟系列之C/C++经典试题(六)

含有指针成员的类的拷贝 题目:下面是一个数组类的声明与实现.请分析这个类有什么问题,并针对存在的问题提出几种解决方案. template<typename T> class Array { public: Array(unsigned arraySize) :data(0), size(arraySize) { if (size > 0) data = new T[size]; } ~Array() { if (data) delete[] data; } void setValue(u

菜鸟系列之C/C++经典试题(五)

求圆圈中剩下的最后一个数字 题目:n个数字(0,1,-,n-1)形成一个圆圈,从数字0开始,每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字).当一个数字删除后,从被删除数字的下一个继续删除第m个数字.求出在这个圆圈中剩下的最后一个数字. 本题就是著名的约瑟夫环问题. 本题的解法我们比较容易想到用链表,当然我们可以自己写一个链表,也可以直接用stl库中的list,实现代码如下: //使用标准库 int JosephusProblem_Solution2(int

菜鸟系列之C/C++经典试题(八)

计算二进制中1的个数 题目:位运算方面的编程很少遇到,但也是很重的一个只是点,一个比较常见的题目就是计算一个数的二进制表示中的1的个数. 分析:一个1都没有就是0, 我们的思路就是一位一位的运算, 我们很快就想到下面的做法: int countBit1(int val) { register int count = 0; while (val) { if (1 & val) { ++count; } val >>= 1; } return count; } 砟一看这个实现不错, 仔细看

菜鸟系列之C/C++经典试题(九)

寻找数组中出现的唯一重复的一个数 题目:1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次.每个数组元素只能访问一次,设计一个算法,将它找出来:不用辅助存储空间,能否设计一个算法实现? 方法一:(当N为比较大时警惕溢出)将1001个元素相加减去1,2,3,--1000数列的和,得到的差即为重复的元素. int findRepeat(int *a) { int i; for (i = 0; i < 1001; i++) { a[1000] += a[i]; }