第2题:写一段程序,找出数组中第k大小的数,输出数所在的位置。例如 {2,4,3,4,7}中,第一大的数是7,位置在4(位置从0开始编号)。第二大、第三大的数都是4,位置在1、3随便输出哪一个均可。第四大的数是3,位置在2。 函数接口为:int find_orderk(const int * narray, const int n, const int k)
第3题:芯片测试。有2k块芯片,已知好芯片比坏芯片多。请设计算法从其中找出一片好芯片,并说明你所用的比较次数上限。其中:好芯片和其它芯片比较时,能正确给出另一块芯片是好还是坏;坏芯片和其它芯片比较时,会随机的给出好或是坏。
思路:
用锦标赛法,
向分成
1024
组,每组两个,如果测试有一个坏的就全部抛弃,然后相邻两组和成一组,从每组里调出一个和另一组的一个进行测试,如果坏的就全部抛弃,依次类推,直到顶点。由于好的比坏的多,每次抛弃一个好的,肯定会抛弃一个坏的,这样就肯定能得到一个好的。
次数为
1024+512+
……
+1
。
1. 请列举你所知道的Linux或者Windows进程间通迅的方式(请选择一种平台回答
,至少回答6个或以上)?
Linux
上的进程间通信有:
管道
消息队列
共享内存
信号
套接字
2. 实现一个TCP端口监听服务进程通常需要使用那些socket函数,并请描述这些函
数的作用?(至少回答6个或以上)
Socket
建立套接字句柄
Bind
绑定这个套接字到服务器地址和端口上
Listen
监听套接字,
Accept
接受连接
Read
读
Write
写
3. 将基于多进程模型的程序移植为基于多线程模型的程序,通常需要如何修改调
整程序(解决那些问题)?
4. 什么是C/C++的模板(template)编程, 有什么好处?
5. 什么是数据库外键?什么是事务?存储过程和触发器的用途?
//这是第二大题编程:
二 编程题:请用c/c++语言编写完整的程序代码,除了实现题目所要求的基本功能
,还应考虑算法的效率和逻辑的严密性.
6. 请编程实现大数阶乘。比如计算2000的阶乘,要求用printf输出结果
数组乘法
int a[N];
int b[N];
int c[2 * N];
void mutiply()
{
for (int i=0; i<N; ++i) {
int carry = 0;
for (int j=0; j<N; ++j) {
carry += a[i]*b[j]+c[i+j];
c[i+j]=carry%10;
carry/=10;
}
}
}
7. 请分别设计一个递归和非递归算法来计算F(n)
F列可递归定义为:
n为大于等于0的整数
F(n)=n 当n=0,1,2
F(n)=F(n-1) - F(n-3) 当n>=3
//
递归版本
..
不考虑高精度大数了,反正这是体力活,以免大家看得辛苦
.
# include <stdio.h>
# include <string.h>
# define MAX 1000000
long long F[MAX];
bool haveBeenCal[MAX]; //
递归防止重复计算
.
long long Cal(int n)
{
if(haveBeenCal[n]) return F[n];
haveBeenCal[n]=1;
return F[n]=Cal(n-1)-Cal(n-3);
}
int main()
{
int n;
memset(haveBeenCal,0,sizeof(haveBeenCal));
haveBeenCal[0]=haveBeenCal[1]=haveBeenCal[2]=1;
F[0]=0,F[1]=1,F[2]=2;
while(scanf("%d",&n)!=EOF) {
if(n<0||n>=MAX) printf("inavailabe n/n");
printf("%lld/n",Cal(n));
}
return 0;
}
//
非递归版本
# include <stdio.h>
# define MAX 100000
int F[MAX];
int main()
{
int n,i,max;
F[0]=0,F[1]=1,F[2]=2;
max=2;
while(scanf("%d",&n)!=EOF) {
if(n<=max) {
printf("%d/n",F[n]);
continue;
}
for(i=max+1;i<=n;i++) F[i]=F[i-1]-F[i-3];
max=n;
printf("%d/n",F[n]);
}
return 0;
}
注
:
对于这一题,我们还有另一个算法,对于一个
n,
不用一步一步地推
n
次,只要
推
lg(n)
次就可以了
.
这是因为
_ _ - - - -
| F[n] | | 1 0 -1 | | F[n-1] |
| F[n-1] | = | 0 0 0 | *| F[n-2] |
| F[n-2] | | 0 1 0 | | F[n-3] |
- - - - - -
再推下去,得
:
_ _ - - ^(n-2) - -
| F[n] | | 1 0 -1 | | F[2] |
| F[n-1] | = | 0 0 0 | * | F[1] |
| F[n-2] | | 0 1 0 | | F[0] |
- - - - - -
于是,问题就转化成如何求一个矩阵的
n
次方了,用分治的思想就可以做到
lg(n)
咯
,好简单,这里略,
可以参考素数判写的
Miller-Rabin
算法
..kaka.
8. 海量单向链表排序
有一单向链表,已知其首指针为head,链表长度为百万量级,链表的每个
结点的结构如下:
typedef struct _node_t {
int a;
struct _node_t* next;
…
} node_t;
试设计程序,将该链表各结点按a值升序排序,要求在原地进行,不允许使
用大量的辅助内存。
//
直接修改一下
Mergesort
就可以了
,nlogn
# include <stdio.h>
typedef struct _node_t{
int a;
struct _node_t* next;
}node_t;
node_t* Mergesort(node_t* L,int count) //
链表头及要链表元数个数
{
if(count==1) return L;
int count1=count/2;
int count2=count-count1;
node_t* p=L;
int i;
for(i=0;i<count1;i++) p=p->next;
node_t* h1=Mergesort(L,count1);
node_t* h2=Mergesort(p,count2);
int t1=0,t2=0;
node_t *head=NULL,*cur;
while(t1<count1||t2<count2) {
if(t1<count1&&t2<count2) {
if(h1->a < h2->a) p=h1,h1=h1->next,t1++;
else p=h2,h2=h2->next,t2++;
}
else if(t1<count1) p=h1,h1=h1->next,t1++;
else p=h2,h2=h2->next,t2++;
if(head==NULL) head=cur=p;
else {
cur->next=p;
cur=p;
}
}
return head;
}
void sort(node_t* &a)
{
int count=0;
node_t* p=a;
while(a) {
count++;
a=a->next;
}
a=Mergesort(p,count);
p=a;
int i;
for(i=0;i<count-1;i++) p=p->next;
p->next=NULL;
}
void input(node_t* &a)
{
a=NULL;
int n;
node_t* p;
while(scanf("%d",&n)!=EOF) {
node_t* s= new node_t;
s->a=n;
if(a==NULL) a=p=s;
else p->next=s;
p=s;
p->next=NULL;
}
}
void output(node_t* a)
{
while(a) {
printf("%d ",a->a);
a=a->next;
}
printf("/n");
}
int main()
{
node_t* a;
//freopen("test.in","r",stdin);
input(a);//
屏幕输入时输一串数字,然后按
enter
再按
ctrl+z
结束
sort(a);
output(a);
return 0;
}
9. 集合合并
给定一个字符串集合,格式如:
{aaa,bbb,ccc},{bbb,ddd},{eee,fff},{ggg},{ddd,hhh}
要求将其中交集不为空的集合合并,要求合并完成后的集合无交集,例如上例应输
出{aaa,bbb,ccc,ddd,hhh},{eee,fff},{ggg}
1)请描述你解决这个问题的思路
2)请给出主要的处理流程,算法,以及算法的复杂度
3)请描述可能的改进(改进的方向如效果,性能等等)
(1)
主要思路
关键就是两步
:
1.
确定那些集合要合成一堆
.
2.
如何合并一堆集合
.
(2)
算法
1.
每个元素对应一个桶,装含有该元素的集合的编号
.
如例子中
:
{aaa}: 1
{bbb}: 1 2
{ccc}: 1
{ddd}: 2 5
{eee}: 3
{fff}: 3
{ggg}: 4
{hhh}: 5
这一步须时
(
所有集合中元素个数的总和
).
2.
然后用并查集,每个桶中相邻的两个集合就要合并
.
如上例,集合
1,2,5
要合并,其它独立
.
这步须时
,
总元素个数
*a(
总元素个数
)
其中
a(n)
是
Ackman
函数的反函数,一般
<=4,
所以也大约地关于总元素个数成线
性
.
3.
合并集合
例如,现在要分别合并集合的编号为
1,2,5
及集合编号为
3,4,6
的集合
.
那么,我们就把集合
1,2,5
中含有的元素都标记为
1.
再把
3,4,6
中含的元素都标记为
2.
然后把所有元素扫一次,按标号分类就可以了
.
复杂度
,O(
总元素个数
).
综上
,
复杂度大约是
O(
总元素个数
).
当然
,
这里不包括由给每个元素编号的复杂度
,
其实复杂度不高的吧
,
就是用最弱智
的方法就可以做到
不同元素的个数
*log(
不同元素的个数
).
(3)
改进
有比上面的算法快的话你通知一声我
,
谢谢
.
一、某密码表以文件的形式存储在硬盘上,文件名为:encrypt.txt,其内容为:
abcdefghijklmnopqrstuvwxyz
ushecginpaywdqmlxbozrtfvjk
其中第一行和第二行分别是原字符和加密后字符的对应,请使用任意一种语言(如PHP、PERL、C/C++、Java等)实现上述加密过程,
如字符"a"替换成字符"u"等,具体的功能要求如下:
·从文件载入上述密码表。
·对指定的文件实施加密,保存在新的文件中。
·请考虑算法的复杂度和异常处理能力。
二、数据库设计题:
请设计一套图书馆借书管理系统的数据库表结构;可以记录基本的用户信息、图书信息、借还书信息;数据表的个数不超过6个;
请画表格描述表结构(需要说明每个字段的字段名、字段类型、字段含义描述);
在数据库设计中应:
1.
保证每个用户的唯一性;
2.
保证每种图书的唯一性;每种图书对应不等本数的多本图书;保证每本图书的唯一性;
3.
借书信息表中,应同时考虑借书行为与还书行为,考虑借书期限;
4.
保证借书信息表与用户表、图书信息表之间的参照完整性;
5.
限制每个用户最大可借书的本数
6.
若有新用户注册或新书入库,保证自动生成其唯一性标识
7.
为以下的一系列报表需求提供支持:
(无特定说明,不需编写实现语句,而需在数据库设计中,保证这些报表可以用最多一条SQL语句实现)
a) 日统计报表:当日借书本数、当日还书本数报表;
b) 实时报表:
i. 当前每种书的借出本数、可借本数;
ii. 当前系统中所有超期图书、用户的列表及其超期天数
iii. 当前系统中所有用户借书的本数,分用户列出(包括没有借书行为的用户);请编写实现此需求的SQL语句:
数据库应用:
请撰写一系列的SQL语句,分别描述完整的借书行为与还书行为;并保证这一系列的SQL语句的执行完整性。
三、日志分析与统计
假设某访问日志文件的格式如下:
2005-01-09 07:02:23 | 127.0.0.1 | c=2 t=4
2005-01-09 17:12:09 | 192.168.0.1 | c=1 t=5
即:每行为一条记录,记录的格式如下:"时间 | IP | c=? t=?",每天所有的访问都记录在一个文件里。
现要求统计如下内容:
·每个IP每天的访问次数;
·每个IP每小时的访问次数;
请考虑统计程序如何实现,给出设计思路和关键算法(可使用伪代码)。可以考虑多种思路,并分析在什么情况(如数据量多少等)
下应使用哪种思路。
实现 void delete_char(char * str, char ch);
把str中所有的ch删掉
void delete_char(char * str, char ch)
{
char *newstr = (char *)malloc(strlen(str)+1);
char *str_p = str;
char *newstr_p = newstr;
//
如果没有这两个的话,
while
后面的指针就出错了
while(*str_p)
{
if(*str_p == ch)
str_p++;
else
*newstr_p++ = *str_p++;
}
*newstr_p = '/0';
strcpy(str, newstr);
free(newstr);
}
编程:
用C语言实现一个revert函数,它的功能是将输入的字符串在原串上倒序后返回。
用C语言实现一个revert函数,它的功能是将输入的字符串在原串上倒序后返回。
void revert(char * str)
{
int length = strlen(str);
char temp;
for(int i = 0; i < length/2; i++)
{
temp = *(str + i);
*(str + i) = *(str + length - 1 - i);
*(str + length - 1 - i) = temp;
}
}
编程:
用C语言实现函数void * memmove(void *dest,const void *src,size_t n)。memmove
在用户输入英文单词时,经常发生错误,我们需要对其进行纠错。假设已经有一个包含了正确英文单词的词典,请你设计一个拼写纠错的程序。
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度;
(3)请描述可能的改进(改进的方向如效果,性能等等,这是一个开放问题)。
(2)
流程 :
每输入一个字母:
沿字典树向下一层,
a )若可以顺利下行,则继续至结束,给出结果;
b) 若该处不能匹配,纠错处理,给出拼写建议 , 继续至 a );
复杂性分析:影响算法的效率主要是字典的实现与纠错处理
( a )字典的实现已有成熟的算法,改进不大,也不会成为瓶颈;
(b) 纠错策略要简单有效 , 如前述情况,是线性复杂度;
要求将其中交集不为空的集合合并
用C语言实现函数void * memmove(void *dest,const void *src,size_t n)。memmove
函数的功能是拷贝src所指的内存内容前n个字节 到dest所指的地址上。
/**
* memmove - Copy one area of memory to another
* @dest: Where to copy to
* @src: Where to copy from
* @count: The size of the area.
*
* Unlike memcpy(), memmove() copes with overlapping areas.
*/
void * memmove(void * dest,const void *src,size_t count)
{
char *tmp, *s;
if (dest <= src) {//dest
在前面,故
dest
头不可能覆盖
src
尾
tmp = (char *) dest;
s = (char *) src;
while (count--)
*tmp++ = *s++;
}
else {//dest
在后面,
dest
头可能覆盖
src
尾,造成
src
没有结束符
tmp = (char *) dest + count;
s = (char *) src + count;
while (count--)
*--tmp = *--s;
}
return dest;
}
3 英文拼写纠错:在用户输入英文单词时,经常发生错误,我们需要对其进行纠错。假设已经有一个包含了正确英文单词的词典,请你设计一个拼写纠错的程序。
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度;
(3)请描述可能的改进(改进的方向如效果,性能等等,这是一个开放问题)。
(1)
思路
:
字典以字母键树组织,在用户输入同时匹配
字典以字母键树组织,在用户输入同时匹配
(2)
流程 :
每输入一个字母:
沿字典树向下一层,
a )若可以顺利下行,则继续至结束,给出结果;
b) 若该处不能匹配,纠错处理,给出拼写建议 , 继续至 a );
算法
:
1. 在字典中查找单词
字典采用 27 叉树组织 , 每个节点对应一个字母 , 查找就是一个字母
一个字母匹配 . 算法时间就是单词的长度 k.
1. 在字典中查找单词
字典采用 27 叉树组织 , 每个节点对应一个字母 , 查找就是一个字母
一个字母匹配 . 算法时间就是单词的长度 k.
2.
纠错算法
情况 : 当输入的最后一个字母不能匹配时就提示出错 , 简化出错处理,动态提示
可能 处理方法 :
(a) 当前字母前缺少了一个字母:搜索树上两层到当前的匹配作为建议;
(b) 当前字母拼写错误:当前字母的键盘相邻作为提示;(只是简单的描述,可
情况 : 当输入的最后一个字母不能匹配时就提示出错 , 简化出错处理,动态提示
可能 处理方法 :
(a) 当前字母前缺少了一个字母:搜索树上两层到当前的匹配作为建议;
(b) 当前字母拼写错误:当前字母的键盘相邻作为提示;(只是简单的描述,可
以有更多的)
根据分析字典特征和用户单词已输入部分选择 (a),(b) 处理
根据分析字典特征和用户单词已输入部分选择 (a),(b) 处理
复杂性分析:影响算法的效率主要是字典的实现与纠错处理
( a )字典的实现已有成熟的算法,改进不大,也不会成为瓶颈;
(b) 纠错策略要简单有效 , 如前述情况,是线性复杂度;
(3)
改进
策略选择最是重要,可以采用统计学习的方法改进。
4 寻找热门查询:
搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串
策略选择最是重要,可以采用统计学习的方法改进。
4 寻找热门查询:
搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串
的长度为1-255字节。假设目前有一千万个记录,
这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个
这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个
。一个查询串的重复度越高,说明查询它的用户越多,
也就是越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G。
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度。
也就是越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G。
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度。
1G就是十亿
(1)
思路:
用哈希做
(2)
首先逐次读入查询串,算哈希值,保存在内存数组中,同时统计频度
(注意值与日志项对应关系)
选出前十的频度,取出对应的日志串,简单不过了。
用哈希做
(2)
首先逐次读入查询串,算哈希值,保存在内存数组中,同时统计频度
(注意值与日志项对应关系)
选出前十的频度,取出对应的日志串,简单不过了。
哈希的设计是关键。
5 集合合并:
给定一个字符串的集合,格式如:
{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}
要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应
5 集合合并:
给定一个字符串的集合,格式如:
{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}
要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应
输出
{aaa bbb ccc ddd hhh},{eee fff}, {ggg}
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度
(3)请描述可能的改进(改进的方向如效果,性能等等,这是一个开放问题)。
{aaa bbb ccc ddd hhh},{eee fff}, {ggg}
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度
(3)请描述可能的改进(改进的方向如效果,性能等等,这是一个开放问题)。
要求将其中交集不为空的集合合并
要求合并完成后的集合无交集
这两个要求只要
1
满足了,
2
就自然满足了。
方法
1
:把每个集合用
vector<vector<char> >
表示,比较两个集合里面
vector<char>
是否相等
(
是否交集不为空,有相同元素
)
,如果有相同集合就合并。这个就是每个集合都和其后面的集合比较,复杂度为
o(n2)
。
方法
2
:把每个集合都遍历一遍,找出所有集合的所有元素
(
不重复
).
然后建立一张表,记录各个元素在每个集合的出现情况,行代表元素,列代表集合,出现则相应位为
1
,否则为
0
。表建立完成后,把每行有多个
1
的
1
所在列的集合合并就可以了。
时间复杂度:
遍历
O(n),
写表
O(n),
最后查表合并
o(n)
,总的复杂度为
O(n)
。
Map的用法
map<string ,int> a;
string b("asdf");
a.insert(map< string,int >::
value_type(b,3));
string c("sdf");
a.insert(map< string,int >::
value_type(c,4));
map<string, int>::iterator i;
for(i = a.begin(); i != a.end(); i++)
cout << (*i).first << " " << (*i).second << endl;
a.erase(b);
for(i = a.begin(); i != a.end(); i++)
cout << (*i).first << " " << (*i).second << endl;
我的方法:
先遍历集合,建立一个
hash
表,
hash
表的键为集合中的字符串,值表示为包含此字符串的集合的序号,比如说集合
1
和
3
包含此元素,值就是
00001010
,复杂度为
o
(
n
)
然后遍历这个
hash
表,将这些值进行
&
运算,结果为
0
时就跳过,结果不为
0
时保存此结果,然后从
hash
表中删除这个条目,遍历完一遍后得到第一个合并后的集合的序号,就进行合并。此为
o(m)
然后再次遍历直到
hash
表为空为止。复杂度合并后集合数乘上
hash
表长度
其实也可以通过一次遍历
hash
表来确定,建立一个链表,
a[0]
为保存
&
不为
0
的值,
a[1]
保存
&
为
0
的值,然后将
a[]
中的数字都与
hash
表的值做
&
运算,都为
0
时将这个值加入链表,不为
0
时,将结果保存到
a[i]
,有可能还要合并,不过这样复杂度也不好算
“已知一个字串由GBK汉字和ansi编码的数字字母混合组成,编写C语言函数实现从中去掉所有ansi编码的的数字和字母(包括大小写).要求在原字串上返回结果。int filter_ansi(char* gbk_string);注:汉字的GBK编码范围是0x8140 - 0xFEFE。”
char * filter_ansi(char* gbk_string)
{
assert(gbk_string!=NULL);
char *src;
char *dst;
src = dst = gbk_string;
while(*src)
{
if(*src & 0x80)
{
*dst++ = *src++;
*dst++ = *src++;
}
else
src++;
}
*dst = '/0';
return gbk_string;
}