编程珠玑学习笔记 Aha算法 思考以及一些代码实现
2012-07-19 18:48
567 查看
"Aproblemthatseemsdifficultmayhaveasimple,unexpectedsolution"
-一个看似难以解决的问题,或许在背后隐藏着一个简单,意想不到的解法
三个引例:
1.输入一个连续文件,文件中存储了之多4,000,000,000个32bit的整数,并且这些整数是按照任意顺序排列的。请你找出一个没有在这个序列中的整数(为什么一定会有至少一个missing的数据呢)。
Q1如果给你足够大的内存,你将如何解决这个问题
Q2.如果仅仅给你一些可以使用的外文件,但是却仅仅几百byte的内存,你将如何解决这个问题。
2.假设输入一个长度为n的字符串,请在第i个位置将字符串旋转。例如n=8,i=3,数组是abcdefgh,旋转以后变成defghabc。你能用O(n)的算法,O(1)的空间解决这个问题吗
3.给出一个英文字典,里面包括单词。请你找出所有的变位词(anagrams),例如pots,stop,tops是变位词。因为组成单词的字母相同,仅仅是排列不同而已。
问题1.
关于第一个问题,首先我们的数据是4billion的,而32bit的数据要有4,294,967,296所以一定是有数据丢失的。而解决这个问题的方法,如果有很大的内存可以使用bitmap辅助的计数排序的方法来找到丢失的数字,使用的内存大概是536M。
第二个问题说如果只有几百byte的内存,和一些连续的文件,这个问题应该如何解决。
这个算法我想了半天,看了一些资料才基本搞明白。
二分原则是这样的:(问题简化,我们假设现在只有3bit的数字,这个时候我们的范围就是0-7,例如丢失的数字是3)
第一步是遍历这个文件,以最高位的值作为依据进行二分。
例如我们的数据是(无序,并且丢失的是3)
6(110)
2(010)
0(000)
1(001)
4(100)
7(111)
5(101)
第一步二分的结果是file1(最高位是1,范围是100-111):6475file2:201.(范围是000-011)
在分割的时候统计,如果文件中的数字的个数小于应该有的数字,这里应该是4个,那么认为里面有数据丢失。所以定位在file2中存在数字丢失。
第二步是在判明已经有丢失的文件中继续寻找,不过这次按照第二位进行二分
在file2中将内容分为
file3:(2)file4:01
这个时候我们发现在file3中的范围内(010-011)内缺少数字。这个时候就可以找到缺少的数字为3了。
这个算法最重要的一点是:它不是一个基于普通排序而定义的二分查找。我们在算法里找不到排序的使用,但是却能找到丢失的数字。
这个要我想到了计数排序,计数排序记录了一个区间内,每个元素值出现的个数。而这个题目是计数在某个区间内元素的个数,然后细分下去。
问题2:
这个编程题目其实蛮经典的,在crackcodeinterview中也有提到。大致的思想如图所示
首先将a[0]存放在临时变量temp中,然后依次将a[i]->a[0]a[2i]->a[i],当然每次使用n*i的时候都要和数组大小size取摩尔运算。最后直到循环会到a[0].
如果发现最后a[1]没有处理,然后将a[1]放入temp重复上面的过程。数学可以证明循环使用的次数是gcd(size,i);其中size为描述中的n,i就是从第i个元素做rotate。
代码编写如下:
[cpp]
viewplaincopyprint?
#include<stdio.h>
template<typenameT>
voidswap(T&a,T&b)
{
Tc=a;
a=b;
b=c;
}
intgcd(intm,intn)
{
if(n>m)
{
swap(m,n);
}
intr;
while(n)
{
r=m%n;
m=n;
n=r;
}
returnm;
}
template<typenameT>
voidRotateArray(TvData[],intnSizeArray,intiRot)
{
intnShift=gcd(nSizeArray,iRot);
for(inti=0;i<nShift;i++)
{
Ttmp=vData[i];
intcur=i,nxt;
intj=0;
while(1)
{
nxt=((j+1)*iRot+i)%nSizeArray;
cur=((j)*iRot+i)%nSizeArray;
if(nxt==i)
{
break;
}
vData[cur]=vData[nxt];
j++;
}
vData[cur]=tmp;
}
}
intmain()
{
chars[10]="abcdefghi";
RotateArray(s,9,5);
printf(s);
return0;
}
关于第二种解法:
假如待旋转的数组是x,可以将数组分成xy看待,a是前面的i个元素,i表示开始旋转那个元素。
例如我们的abcdefg如果i为3那么x=abcy=defg。这样的话问题的本质可以理解为将xy通过交换变成yx。
假设y的长度比x要长,所以可以将其表示为x(yl)(yr),并且约定yr的长度和x一样。交换x和yr部分得到(yr)(yl)x,下面我们要做的是将(yr)(yl)交换,变成(yl)(yr)。这个时候就可以用递归的思路来求解了。
[cpp]
viewplaincopyprint?
template<typenameT>
voidRotateArray(TvData[],intf,intm,intt)
{
intnL=m-f;
intnR=t-m+1;
inti,j;
if(nL>nR)
{
for(i=f,j=m;j<=t;i++,j++)
{
swap(vData[i],vData[j]);
}
RotateArray(vData,f+nR,m,t);
}
elseif(nL<nR)
{
for(i=f,j=t-nL+1;i<m;i++,j++)
{
swap(vData[i],vData[j]);
}
RotateArray(vData,f,m,t-nL);
}
elseif(nL==nR)
{
for(i=f,j=t;i<j;i++,j--)
{
swap(vData[i],vData[j]);
}
}
}
解法3.不过这个代码确实没有aha的感觉,也许应该叫ouch吧,没错。利用上面的方法确实需要心思细腻的编程,事实上可以有更aha的算法
将ab变成ba,其实可以用如下方式来做操作,首先将reverse(a),reverse(b),最后reverse(ab)。哈哈是不是有aha的感觉了。
问题三:
这个题目是我的第一感觉是用哈希表,将每个字符串求一个哈希值。但是我没有特别想通如何将spot和pots映射到同一个哈希的slot里。
作者给出的思路其实也满类似。
1.为每个单词做签名
2.根据签名排序。
而作者在这里所使用的签名也是很简单的方法,就是一个单词的字母序的重排。例如将spot->opst而pots的签名也是opst,所以他们会有相同的排名。
如果是我设计数据结构,我会写成
structWordPair
{
charstrWord[MAX_PATH];
charstrSigniture[MAX_PATH];
}
程序第一步输入所有的字符串,同时求出所有的signiture,然后按照签名排序,就可以得到同构的词汇了。
这给我的哈希法一些提示,如果先排序,然后再哈希不就是我那个的解决方案了吗~
-一个看似难以解决的问题,或许在背后隐藏着一个简单,意想不到的解法
三个引例:
1.输入一个连续文件,文件中存储了之多4,000,000,000个32bit的整数,并且这些整数是按照任意顺序排列的。请你找出一个没有在这个序列中的整数(为什么一定会有至少一个missing的数据呢)。
Q1如果给你足够大的内存,你将如何解决这个问题
Q2.如果仅仅给你一些可以使用的外文件,但是却仅仅几百byte的内存,你将如何解决这个问题。
2.假设输入一个长度为n的字符串,请在第i个位置将字符串旋转。例如n=8,i=3,数组是abcdefgh,旋转以后变成defghabc。你能用O(n)的算法,O(1)的空间解决这个问题吗
3.给出一个英文字典,里面包括单词。请你找出所有的变位词(anagrams),例如pots,stop,tops是变位词。因为组成单词的字母相同,仅仅是排列不同而已。
问题1.
关于第一个问题,首先我们的数据是4billion的,而32bit的数据要有4,294,967,296所以一定是有数据丢失的。而解决这个问题的方法,如果有很大的内存可以使用bitmap辅助的计数排序的方法来找到丢失的数字,使用的内存大概是536M。
第二个问题说如果只有几百byte的内存,和一些连续的文件,这个问题应该如何解决。
这个算法我想了半天,看了一些资料才基本搞明白。
二分原则是这样的:(问题简化,我们假设现在只有3bit的数字,这个时候我们的范围就是0-7,例如丢失的数字是3)
第一步是遍历这个文件,以最高位的值作为依据进行二分。
例如我们的数据是(无序,并且丢失的是3)
6(110)
2(010)
0(000)
1(001)
4(100)
7(111)
5(101)
第一步二分的结果是file1(最高位是1,范围是100-111):6475file2:201.(范围是000-011)
在分割的时候统计,如果文件中的数字的个数小于应该有的数字,这里应该是4个,那么认为里面有数据丢失。所以定位在file2中存在数字丢失。
第二步是在判明已经有丢失的文件中继续寻找,不过这次按照第二位进行二分
在file2中将内容分为
file3:(2)file4:01
这个时候我们发现在file3中的范围内(010-011)内缺少数字。这个时候就可以找到缺少的数字为3了。
这个算法最重要的一点是:它不是一个基于普通排序而定义的二分查找。我们在算法里找不到排序的使用,但是却能找到丢失的数字。
这个要我想到了计数排序,计数排序记录了一个区间内,每个元素值出现的个数。而这个题目是计数在某个区间内元素的个数,然后细分下去。
问题2:
这个编程题目其实蛮经典的,在crackcodeinterview中也有提到。大致的思想如图所示
首先将a[0]存放在临时变量temp中,然后依次将a[i]->a[0]a[2i]->a[i],当然每次使用n*i的时候都要和数组大小size取摩尔运算。最后直到循环会到a[0].
如果发现最后a[1]没有处理,然后将a[1]放入temp重复上面的过程。数学可以证明循环使用的次数是gcd(size,i);其中size为描述中的n,i就是从第i个元素做rotate。
代码编写如下:
[cpp]
viewplain
#include<stdio.h>
template<typenameT>
voidswap(T&a,T&b)
{
Tc=a;
a=b;
b=c;
}
intgcd(intm,intn)
{
if(n>m)
{
swap(m,n);
}
intr;
while(n)
{
r=m%n;
m=n;
n=r;
}
returnm;
}
template<typenameT>
voidRotateArray(TvData[],intnSizeArray,intiRot)
{
intnShift=gcd(nSizeArray,iRot);
for(inti=0;i<nShift;i++)
{
Ttmp=vData[i];
intcur=i,nxt;
intj=0;
while(1)
{
nxt=((j+1)*iRot+i)%nSizeArray;
cur=((j)*iRot+i)%nSizeArray;
if(nxt==i)
{
break;
}
vData[cur]=vData[nxt];
j++;
}
vData[cur]=tmp;
}
}
intmain()
{
chars[10]="abcdefghi";
RotateArray(s,9,5);
printf(s);
return0;
}
关于第二种解法:
假如待旋转的数组是x,可以将数组分成xy看待,a是前面的i个元素,i表示开始旋转那个元素。
例如我们的abcdefg如果i为3那么x=abcy=defg。这样的话问题的本质可以理解为将xy通过交换变成yx。
假设y的长度比x要长,所以可以将其表示为x(yl)(yr),并且约定yr的长度和x一样。交换x和yr部分得到(yr)(yl)x,下面我们要做的是将(yr)(yl)交换,变成(yl)(yr)。这个时候就可以用递归的思路来求解了。
[cpp]
viewplain
template<typenameT>
voidRotateArray(TvData[],intf,intm,intt)
{
intnL=m-f;
intnR=t-m+1;
inti,j;
if(nL>nR)
{
for(i=f,j=m;j<=t;i++,j++)
{
swap(vData[i],vData[j]);
}
RotateArray(vData,f+nR,m,t);
}
elseif(nL<nR)
{
for(i=f,j=t-nL+1;i<m;i++,j++)
{
swap(vData[i],vData[j]);
}
RotateArray(vData,f,m,t-nL);
}
elseif(nL==nR)
{
for(i=f,j=t;i<j;i++,j--)
{
swap(vData[i],vData[j]);
}
}
}
解法3.不过这个代码确实没有aha的感觉,也许应该叫ouch吧,没错。利用上面的方法确实需要心思细腻的编程,事实上可以有更aha的算法
将ab变成ba,其实可以用如下方式来做操作,首先将reverse(a),reverse(b),最后reverse(ab)。哈哈是不是有aha的感觉了。
问题三:
这个题目是我的第一感觉是用哈希表,将每个字符串求一个哈希值。但是我没有特别想通如何将spot和pots映射到同一个哈希的slot里。
作者给出的思路其实也满类似。
1.为每个单词做签名
2.根据签名排序。
而作者在这里所使用的签名也是很简单的方法,就是一个单词的字母序的重排。例如将spot->opst而pots的签名也是opst,所以他们会有相同的排名。
如果是我设计数据结构,我会写成
structWordPair
{
charstrWord[MAX_PATH];
charstrSigniture[MAX_PATH];
}
程序第一步输入所有的字符串,同时求出所有的signiture,然后按照签名排序,就可以得到同构的词汇了。
这给我的哈希法一些提示,如果先排序,然后再哈希不就是我那个的解决方案了吗~
相关文章推荐
- 设计模式学习笔记以及java代码实现
- 关于LDA学习的一些有用的博客以及大牛写的代码实现
- unity3D学习【功能实现】之十:关卡制作scroll rect应用以及一些代码
- Android(java)学习笔记66:线程的实现方案2思路以及代码实现
- C/C++中strlen(),strcpy(),strcat()以及strcmp()的代码实现--学习笔记
- 关于代码阅读的编程实现技巧及自己工作学习的一些经验(推荐看后面的后记)
- 第96讲:Akka第一个案例动手实战main方法实现中ActorSystem等代码详解学习笔记
- java学习笔记--关于interface和abstract的一些思考
- ZooKeeper学习笔记:使用zookeeper的API实现增删查改以及客户端的观察者模式
- 【学习笔记】android动画的不同实现方式以及要点
- 【Java反射学习笔记系列之jdk动态代理】静态代理和动态代理的区别以及动态代理的作用和实现
- 【Java数据结构学习笔记之一】线性表的存储结构及其代码实现
- 精确覆盖问题学习笔记(五)——优化算法的实现代码
- 糊糊的前端学习笔记——25行代码实现一个贪吃蛇小游戏【Day06】
- C#开发学习笔记:C#中实现两个GridControl之间的数据拖拽以及同一个GridControl中的数据行上下移动
- google_v8学习笔记:NO1 环境搭建以及代码获取
- 学习笔记:使用Web Service Software Factory开发简易留言本服务以及Mobile调用实现-1.创建Service
- 学习笔记_用hibernateQBC动态查询+jsp的jstl标签库实现的动态查询以及分页特效
- 学习笔记之 自定义连接池实现代码
- Coursera deeplearning.ai 深度学习笔记1-2-Neural Network Basics-逻辑回归原理推导与代码实现