百度在线笔试

上上周百度又让我参加了一轮在线笔试。今天忽然在桌面上看到于是就贴出来。
第一部分、算法与程序设计
1.在一棵一般的二叉树中找到指定的元素,如果有重复出现的元素,要求元素为深度最深的任何一个。指定元素找不到时返回EMPTY_NODE,请用C语言实现,相关数据结构与函数声明如下:
struct Node
{
int iValue;
int id;
Node *pLeft;
Node *pRight;
};
const Node EMPTY_NODE = {0, 0, NULL, NULL};
Node findDeepest(Node *pRoot, int iWanted); //pRoot为根节点,wanted为指定元素的iValue
2.一个单词字典库,单词个数约为10万,每个单词长度不超过16,单词都是由小写字母组成,同时给出16个小写字母,请设计一种高效算法来找到用这些给出字母拼出一个字典中最大长度的单词。给出的16个字母每个字母最多使用一次,也可以不使用。存在多解的时候给出任意一个最优答案就行。
例如:给出adeenrstuvxyzuki可以拼出adventures
请详细描述你的算法思路(如需要,可给出代码\伪代码来辅助描述),并分析其时间复杂度。最后请分析下你的算法以及数据结构的优缺点,存在哪些可改进的地方。
第二部分、系统设计题
1.       有200亿条数据,每条数据的大小在1K~1M不等,每条数据有一个唯一的u_int64的id。
请设计一个读取数据系统,能根据id获取数据。要求:
A.        内存有限制,16G
B.        尽可能利用内存资源
C.        尽可能高效的获取数据
D.        可以利用磁盘,磁盘容量不受限制
2.       C2C网站的商品子系统,包括的关系数据有 分类、属性、商品。
一个商品只能属于一个分类,不同的分类有不同的属性(多个),每个属性有多个候选属性值,其中分类、属性、属性值的更新频率较低。
一个商品的属性,是所属分类的属性,属性值是候选属性值中的一个或多个。
例如:
分类:衣服
属性:尺寸、颜色
尺寸的候选属性值:S/M/L/XL/XXL/XXXL
颜色的候选属性值:黑/白/红/黄/蓝
商品:衣服A,尺寸S,颜色黑
另外,商品还有卖家、价格等其它信息
请设计商品子系统的存储结构或数据库结构。要求:
A.        能够正确维护分类、属性、商品之间的关系数据
B.        尽量减少冗余
C.        考虑数据的增、删、改、查操作,效率尽可能高
D.        能够按照卖家查询出其发布的所有商品
==============问题和解答的分割线======================
1111111111111111111111111111111111111111111111111111111111111111111111111111
Node findDeepest(Node *pRoot, int iWanted); //pRoot为根节点,wanted为指定元素的iValue
{
return findDeepestWithDepth(pRoot, iWanted, 1);
}
Node findDeepestWithDepth(Node *pRoot, int iWanted, int depth)
{
static int maxDepth = 1;
static Node deepestNode = EMPTY_NODE;
if(pRoot != null)
{
Node l = findDeepestWithDepth(pRoot->pLeft, iWanted, depth+1);
Node r = findDeepestWithDepth(pRoot->pRight, iWanted, depth+1);
if(isEmpty(l) && isEmpty(r))
{
if(maxDepth < depth)
{
maxDepth = depth;
node = *pRoot;
return *pRoot;
}
else
{
return deepestNode;
}
}
else
{
return deepestNode;
}
}
else
{
return EMPTY_NODE;
}
}
bool isEmpty(Node node)
{
return node.iValue == 0 && node.id == 0 && node.pLeft == NULL && node.pRight == NULL;
}
2222222222222222222222222222222222222222222222222222222222222222222222222
1. 对字典中每一个单词进行遍历,计算出每个单词每个字母的个数和单词的长度
2. 针对每个单词里每个字母(a-z)的个数和单词的长度建立索引,类似数据库的一张表,表一共有28个列(加上一个隐含的rowid),包括单词内容(1列),26个字母每个字母的个数(26列)和单词的长度
3. 搜索时,对给定的字符串进行相同的统计,计算出每个字母的个数。根据统计结果去搜索个数小于给定字符串字母的单词的rowid,最多16次搜索,搜索后对结果进行交集(思想类似: select word from words where a <= 1 and b <= 1 and d <= 2 and …)
4. 对最后的交集根据长度,得出最长的单词的rowid,最终得出单词
时间复杂度: 16次搜索,每次为O(log n),最多15次的交集运算,复杂度为O(logn * logn),最后寻找最大值可以忽略,所以时间复杂度为O(logn*logn)
改进:提高索引效率,如联合索引
33333333333333333333333333333333333333333333333333333333333333333333333333
1. 内存中使用二叉搜索树进行索引,每个节点占用16字节(2个指针+1个id),可放10亿个节点,则底层节点有5亿个。底层节点左右为空,8个字节用来表示记录在磁盘的存储位置
2. 磁盘存储分为5亿块,每个块里有40条记录
3. 块分为块头索引和块内容。块头索引按id顺序排列,包括了该块的id,起始位置和长度。块头大小为40*(8+8+4)=800字节
4. 每次通过id访问数据,首先查找二叉搜索树,经过29次内存比较得到索引块,再载入索引块头,使用二分搜索得到内容的起始位置和长度,最终得到内容。一共35次左右内存访问和2次磁盘访问
44444444444444444444444444444444444444444444444444444444444444444444444444444
商品表 product: id, name, seller, price, category
分类表 category: id, name
属性表 attribute: id, name, category_id, type
属性候选值表 attribute_value: id, attribute_id, value
商品属性表 product_value: product_id, attribute_value_id

OpenJDK6 build小记(Ubuntu 9.10)

之前在twitter上喊喊要研究JVM,今天算是迈出了第一步,从源代码编译openjdk。openjdk现在有6和7两个版本下载,现在7还在milestone 6的阶段,也暂时没什么需要尝试的新特性,另外openjdk6的代码大小只有openjdk7的一半(近50M对114M),于是选择了openjdk6来进行构建。另外,jdk6提供官方下载,这样也方便了和官方版本进行对比。
事实上,根据官方的描述,openjdk6的代码是基于jdk7 b20和jdk6的update的,openjdk7的代码是基于jdk7 b10,很奇怪的代码来源。因为sun是在jdk6开发的晚期才宣布java的开源,于是先开源了java7成为openjdk7,然后再发布了jdk6之后才重新整理代码,从jdk7 b20里剔除了java7特性的代码,发布了openjdk6。现在jdk7的概念等同于openjdk7,但jdk6却和openjdk6不是一个东西。
openjdk的主页的左边栏有众多的链接,主要分为Groups和Projects,似乎是有一些工作组从事不同的项目。不少栏目里都有很多有用的资料,有兴趣的可以看看。其中有一个Build的工作组,负责构建工作,里面有关于构建的官方指南
代码的下载可以用Mecurial来下,也可以下打好的bundle,应该大部分人会选择后者,Mecurial毕竟还是小众的版本配置工具,需要python。
代码构建的过程基本是按照官方指南一步步来的。我的构建环境是虚拟机中的Ubuntu 9.10(BS一下自己,硬盘上就有早就装好的Ubuntu 8.10,只是因为懒得离开Windows)。除了Linux平台,openjdk6还支持在Solaris和Windows上的构建。Linux使用gcc(4.2)编译,Windows使用VS2003(也有2005成功的例子,。而openjdk7的构建文档直接要求VS2008)。GNU make是构建工具,所以Windows下还需要cygwin。
构建的依赖在ubuntu下很简单,用下面的语句搞定。在9.10下需要下载很多东西,最大的是llvm的binary和开发包,不知是用来做编译还是虚拟机的。

sudo aptitude build-dep openjdk-6

另外需要安装openjdk6当作bootstrap jdk,还需要libmotif开发包

sudo aptitude install openjdk-6-jdk libmotif-dev

接着,设置一下环境变量

export LANG=C ALT_BOOTDIR=/usr/lib/jvm/java-6-openjdk

然后直接在源代码的目录下运行:

make all

就开始构建openjdk6了。昨天我没好好看文档,自己去设定了motif, binary plugs, freetype的环境。还在错误的目录下运行了make,因为在子目录下make是部分构建,所以一直报错,找不到ALT_JDK_IMPORT_PATH。最后也还是自己折腾好了,但不知道是ubuntu早就下好了依赖,还是自己设置好的。
构建完成后,可以自己运行代码目录下build/linux-i586/bin下的可执行程序,比如java
这个时候版本号成了

./java -version
openjdk version “1.6.0-internal”
OpenJDK Runtime Environment (build 1.6.0-internal-marshall_15_nov_2009_21_53-b00)
OpenJDK Client VM (build 14.0-b16, mixed mode)

对比原有的信息:

java -version
java version “1.6.0_0”
OpenJDK Runtime Environment (IcedTea6 1.6.1) (6b16-1.6.1-3ubuntu1)
OpenJDK Client VM (build 14.0-b16, mixed mode, sharing)

另:在我分配了512M内存的ubuntu上,编译时间大致为1个小时。构建有警告提示内存太少,会影响速度。明天放到非虚拟的ubuntu下跑试试看。