题目一:中国有13亿人,怎样找出重复最多的名字?
http://topic.youkuaiyun.com/u/20071111/14/c9d61173-ba2e-4ab9-9d8b-439c47597646.html
Answer1:
首先,排除那些长度大于3的名字,因为生活中最常见的名字是2个字或3个字的,长度大于3的名字不常见。
然后,从姓分析。
最常见的名字的姓必然在百家姓中排名靠前,因此只要考虑名字的姓以百家姓中的就可以了。
然后考虑其他的。
找一些名字中的常用字,这些字基本上都是好字,数量不会很多,并将这些字建一个Hash表(按汉字编码来hash,若这个字是好字,那么将对应位置置1,否则置0)。
扫一遍全国人民的名字,分析其中的姓是否在百家姓中、名字中的字是否是好字。若两个条件满足,则按姓氏留下该名字(即同姓的放在一个文件中),否则,剔除该名字。
GB 2312-80 把收录的汉字分成两级。第一级汉字是常用汉字,计 3755 个,置于16~55区,按汉语拼音字母/笔形顺序排列
GB 2312-80 图形字符代码表
01-07区 08-15区 16-23区 24-31区 32-39区 40-47区
48-55区 56-63区 64-71区 72-79区 80-87区 88区以下略
结合汉字编码表,可以设计一个算法来解决问题。
因为名字一般是常用汉字,所以在剩下的按姓氏分的名字中,由于同一个文件姓一样,所以同一个文件中的名只能有3755*3755种可能,为方便计算,我们将它放大为4096*4096,因此,可以开一个长度为 4096*4096 Hash表:
int Hash[4096*4096],每一个可能的名字对应着表的一个下标索引。
对每一个姓文件,都进行如下操作:
开始时将Hash[]全部清零。
然后就开始扫描,将每个名字转化为一个数字index,++Hash[index]。
最后,在Hash[]中找到最大者及其对应的下标索引k就可以了,那么,在这个姓中出现次数最多的名,可以由下标索引k得到。
空间要求是4096*4096*4=16777216*4=64M,不算太大。
Answer2:
试试这个算法: 线性的
假设汉字编码为GB2312十六进制编码,那么一个名字在计算机内存储表示为一系列的二进制位。譬如 “张三“,应当是一个32位的二进制位串,“张三丰”应当是一个48位的二进制位串。(一个汉字占用两个字节)由于编码严格,所以名字不同,编码绝对不同。
算法思想:
建立一棵二叉树,类似哈夫曼编码树,遇0转向左子树,遇1转向右子树。每个节点设置一个计数器 count, 保存从根节点到该节点路径所代表的位串(其实代表一个名字)的重复次数。
对于每个名字,转化为位串,沿二叉树到达对应节点,将其计数器加一,若比当前最大重复还大,则更新当前最大。当然同时这也是一个建树的过程,如果第一次遇到就建立路径了。
相信中国人名字都不长,平均长度为3个汉字吧,则访问该树的平均深度为3*16 = 48,常数级。
复杂度分析: 时间复杂度 O(n) ,这里n为中国人的名字总个数;
空间复杂度,一棵深度不大的树而已,不会超过 2^m ,这里m为最大二进制位串长,即最大深度。
进一步提高效率:
将二进制串转化为八进制串或十六进制串,可以减小深度。当然这时候就是B树了。
八进制串时平均访问深度为 6
十六进制串时平均访问深度为 3
题目二:
http://blog.youkuaiyun.com/dremi/archive/2008/09/22/2963912.aspx
百度笔试题:
#include <stdio.h>
int main()
{
int x;
x = 4;
x += x-=x-x--;
cout<<x<<endl;
return 0;
}
x得结果是什么?
Answer1:
让我们来分析一下:
首先执行到07行代码时,先是取出x的数据即为4,然后执行x-x ,4 - 4 = 0;
其实这个时候 (x -= x-x--)中第一个x得值也为4,即他们3个值刚开始是相同的,也就是这个等式得
x = x - (x - x--);
所以执行到(x -= x-x--)时,x的值变为4, 此时在执行x += x,所以x现在变为8,但别忘了最后还有一个x--要执行。
所以最终结果是7
所以这个等式最终可以化成这样的:
x = x + (x = x - (x - x--));
从vc2005的汇编代码可以得出:x = x + (x = x - (x - x--))和 (x += x-=x-x--);是一模一样的:
00414BFE mov dword ptr [x],4
x += x-=x-x--;
00414C05 mov eax,dword ptr [x]
00414C08 sub eax,dword ptr [x] //x -x
00414C0B mov ecx,dword ptr [x]
00414C0E sub ecx,eax //x -= (x-x)
00414C10 mov dword ptr [x],ecx //更新x
00414C13 mov edx,dword ptr [x] //取出x的值
00414C16 add edx,dword ptr [x] //计算x += x;
00414C19 mov dword ptr [x],edx //更新x
00414C1C mov eax,dword ptr [x]
00414C1F sub eax,1 //x--
00414C22 mov dword ptr [x],eax //更新x
cout<<x<<endl;
00414BFE mov dword ptr [x],4
x = x + (x = x - (x - x--));
00414C05 mov eax,dword ptr [x]
00414C08 sub eax,dword ptr [x]
00414C0B mov ecx,dword ptr [x]
00414C0E sub ecx,eax
00414C10 mov dword ptr [x],ecx
00414C13 mov edx,dword ptr [x]
00414C16 add edx,dword ptr [x]
00414C19 mov dword ptr [x],edx
00414C1C mov eax,dword ptr [x]
00414C1F sub eax,1
00414C22 mov dword ptr [x],eax
cout<<x<<endl;
当把x += x-=x-(--x)时,答案就变成6了
他可以扩展成这样:x = x + (x = x - (x - (--x)));
即:x = x + (x = 3 - (3 - 3));
x = 3 + 3;
x = 6;
这个的汇编代码如下:它首先把x减1了
00414BFE mov dword ptr [x],4
x += x-=x-(--x);
00414C05 mov eax,dword ptr [x]
00414C08 sub eax,1 //取出值就把x减1
00414C0B mov dword ptr [x],eax //更新x
00414C0E mov ecx,dword ptr [x] //
00414C11 sub ecx,dword ptr [x] //x - x
00414C14 mov edx,dword ptr [x]
00414C17 sub edx,ecx x -= (x -x)
00414C19 mov dword ptr [x],edx //更新x
00414C1C mov eax,dword ptr [x]
00414C1F add eax,dword ptr [x] x += x
00414C22 mov dword ptr [x],eax //更新x
cout<<x<<endl;