使用“初中知识”实现查找重复最优算法 + 最终极限算法
2009-08-08 16:39 鹤冲天 阅读(3037) 评论(8) 编辑 收藏 举报这是园子里讨论了好长时间的题目了:
1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,能否设计一个算法实现?
发起文章:算法题,求高手. 作者: 莫贝特(MBetter)
算法改进:利用异或的特性解决,找出重复数的问题,应该是目前最优算法。 作者:Ivony...
莫贝特给出的算法是:将所有数加起来,减去1+2+...+1000的和
Ivony给出的算法是: 将所有的数全部异或,得到的结果与1^2^3^...^1000的结果进行异或,得到的结果就是重复数
两位算法的时间复杂度都是: 2n (莫贝特的算法可以用高斯算法简化)
我把题目扩展了一下,将1000换成n,让些题更通用一些:
1-n放在含有n+1个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,能否设计一个算法实现?(n可能为奇数)(注意:数组是无序的,有序数组很好解决不讨论)
下面是我的算法,先给出代码,c#语言
1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,能否设计一个算法实现?
发起文章:算法题,求高手. 作者: 莫贝特(MBetter)
算法改进:利用异或的特性解决,找出重复数的问题,应该是目前最优算法。 作者:Ivony...
莫贝特给出的算法是:将所有数加起来,减去1+2+...+1000的和
Ivony给出的算法是: 将所有的数全部异或,得到的结果与1^2^3^...^1000的结果进行异或,得到的结果就是重复数
两位算法的时间复杂度都是: 2n (莫贝特的算法可以用高斯算法简化)
我把题目扩展了一下,将1000换成n,让些题更通用一些:
1-n放在含有n+1个元素的数组中,只有唯一的一个元素值重复,其它均只出现一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空间,能否设计一个算法实现?(n可能为奇数)(注意:数组是无序的,有序数组很好解决不讨论)
下面是我的算法,先给出代码,c#语言
/// <summary>
/// 查找重复的数
/// </summary>
/// <param name="ints">1..n无序序列加上1..n之间的一个数</param>
/// <returns>重复的数</returns>
public static int GetRepeated(int[] array)
{
int temp = 0;
foreach (int i in array)
if (i % 2 == 1) temp += i + 1;
else temp -= i;
if (array.Length % 2 == 0) temp -= array.Length;//n为奇数要处理一下
return temp < 0 ? -temp : temp - 1;
}
原理如下(n为偶数时):/// 查找重复的数
/// </summary>
/// <param name="ints">1..n无序序列加上1..n之间的一个数</param>
/// <returns>重复的数</returns>
public static int GetRepeated(int[] array)
{
int temp = 0;
foreach (int i in array)
if (i % 2 == 1) temp += i + 1;
else temp -= i;
if (array.Length % 2 == 0) temp -= array.Length;//n为奇数要处理一下
return temp < 0 ? -temp : temp - 1;
}
(1-2)+(3-4)+(5-6)+(7-8)+....+(n-1 - n) 等于多少?先不管,再改进一下
((1+1)-2)+((3+1)-4)+((4+1)-5)+((5+1)-6)+...+((n-1+1) - n) 等于多少?零!这个公式证明初中生都会!
这是有序序列,对无序的情况我们可以这样理解:从数组中依次读数,如果读到是奇数则加上再加1,如果偶数则减去。
和代码中的foreach对照一下吧,应该比较好理解!
函数中倒数第二行对n是奇数的情况作了些处理。
最后返回处理:
如果重复的数是偶数,则肯定被减了,则返回它的负值-temp;
如果重复的数是奇数,则肯定被加了,而且还多加了1, 返回 temp-1。
这个算法时间复杂度是n,空间复杂度是1,代码行数是6,原理也简单。
利用初中(小学奥赛估计也会)的知识解决一个算法问题,感觉算不上高手。只能感叹我们以前的知识忘记了太多了!
----------------------------------------------------------------------------------------------------------
刚才又看前面两篇帖子的回复,发现了loogn 提供的一个算法,本人修正了一下:
public static int GetRepeated2(int[] array)
{
int temp = 0;
for (int i = 0; i < array.Length; i++)
temp += array[i] - i ;
return temp;
}
算法原理:改进莫贝特的算法,加减加减加减...,而不是(加加加...)减(加加加...)。出现溢出的可能性也小了很多!{
int temp = 0;
for (int i = 0; i < array.Length; i++)
temp += array[i] - i ;
return temp;
}
更是精简,强人!!
不甘落后,我用lambda再改进一下:
public static int GetRepeated3(int[] array)
{
return array.Select((i, j) => i - j).Sum();
}
简单说明一下:{
return array.Select((i, j) => i - j).Sum();
}
方法三中的“i”相当与方法二中的“array[i]”;
方法三中的“j”相当于方法二中的“i”
其它大可不必写成一个函数,像下面这样直接调用就好了,也算是最后的极限算法了。
int repeatedNum = array.Select((i, j) => i - j).Sum();
array就是包含n+1个数的数组,repeatedNum就是其中唯一重复的数。这应该是极限了吧!大家可以调试运行一下这几种方法都可以通过,有问题拍我。
本人系列文章《c#扩展方法奇思妙用》,敬请关注!
-------------------
思想火花,照亮世界