编程:
用C语言实现一个revert函数,它的功能是将输入的字符串在原串上倒序后返回。
2 编程:
用C语言实现函数void * memmove(void *dest,const void *src,size_t n)。memmove
函数的功能是拷贝src所指的内存内容前n个字节
到dest所指的地址上。
3 英文拼写纠错:
在用户输入英文单词时,经常发生错误,我们需要对其进行纠错。假设已经有一个包
含了正确英文单词的词典,请你设计一个拼写纠错
的程序。
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度;
(3)请描述可能的改进(改进的方向如效果,性能等等,这是一个开放问题)。
4 寻找热门查询:
搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串
的长度为1-255字节。假设目前有一千万个记录,
这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个
。一个查询串的重复度越高,说明查询它的用户越多,
也就是越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G。
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度。
5 集合合并:
给定一个字符串的集合,格式如:
{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}
要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应
输出
{aaa bbb ccc ddd hhh},{eee fff}, {ggg}
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度
(3)请描述可能的改进(改进的方向如效果,性能等等,这是一个开放问题)。
1
1 题
char *revert(char * str)
{
int n=strlen(str);
int i=0;
char c;
for(i=0;i {
c=str;
str=str[n-i];
str[n-i]=c;
}
return str;
}
///
2 题
void * memmove(void *dest,const void *src,size_t n)
{
assert((dest!=0)&&(src!=0));
char * temp=(char * )dest;
char * ss=(char * )src;
int i=0;
for(;i {
*temp++=*ss++;
}
return temp;
}
/
3 题
(1)思路 :
字典以字母键树组织,在用户输入同时匹配
(2)
流程:
每输入一个字母:
沿字典树向下一层,
a)若可以顺利下行,则继续至结束,给出结果;
b)若该处不能匹配,纠错处理,给出拼写建议,继续至a);
算法:
1.在字典中查找单词
字典采用27叉树组织,每个节点对应一个字母,查找就是一个字母
一个字母匹配.算法时间就是单词的长度k.
2.纠错算法
情况:当输入的最后一个字母不能匹配时就提示出错,简化出错处理,动态提示
可能 处理方法:
(a)当前字母前缺少了一个字母:搜索树上两层到当前的匹配作为建议;
(b)当前字母拼写错误:当前字母的键盘相邻作为提示;(只是简单的描述,可
以有更多的)
根据分析字典特征和用户单词已输入部分选择(a),(b)处理
复杂性分析:影响算法的效率主要是字典的实现与纠错处理
(a)字典的实现已有成熟的算法,改进不大,也不会成为瓶颈;
(b)纠错策略要简单有效 ,如前述情况,是线性复杂度;
(3)改进
策略选择最是重要,可以采用统计学习的方法改进。
//
4 题
(1)思路:
用哈希做
(2)
首先逐次读入查询串,算哈希值,保存在内存数组中,同时统计频度
(注意值与日志项对应关系)
选出前十的频度,取出对应的日志串,简单不过了。
哈希的设计是关键。
//
5 题
(1)思路:先将集合按照大小排列后,优先考虑小的集合是否与大的集合有交集。有
就合并,如果小集合与所有其他集合都没有交集,则独立。独立的集合在下一轮的比
较中不用考虑。这样就可以尽量减少字符串的比较次数。当所有集合都独立的时候,
就终止。
(2)处理流程:
1.将集合按照大小排序,组成集合合并待处理列表
2.选择最小的集合,找出与之有交集的集合,
如果有,合并之;
如果无,则与其它集合是独立集合,从待处理列表 中删除。
3.重复直到待处理列表为空
算法:
1。将集合按照大小从小到大排序,组成待处理的集合列表。
2。取出待处理集合列表中最小的集合,对于集合的每个元素,依次在其他集合中搜索
是否有此元素存在:
1>若存在,则将此小集合与大集合合并,并根据大小插入对应的位置 。转3
。
2>若不存在,则在该集合中取下一个元素。如果无下一个元素,即所有元素
都不存在于其他集合。则表明此集合独立,从待处理集合列表中删除。并加入结果集
合列表。转3。
3。如果待处理集合列表不为空,转2。
如果待处理集合列表为空,成功退出,则结果集合列表就是最终的输出。
算法复杂度分析:
假设集合的个数为n,最大的集合元素为m
排序的时间复杂度可以达到n*log(n)
然后对于元素在其他集合中查找,最坏情况下为(n-1)*m
查找一个集合是否与其他集合有交集的最坏情况是m*m*(n-1)
合并的时间复杂度不会超过查找集合有交集的最坏情况。
所以最终最坏时间复杂度为O(m*m*n*n)
需要说明的是:此算法的平均时间复杂度会很低,因为无论是查找还是合并,都是处
于最坏情况的概率很小,而且排序后优先用最小集合作为判断是否独立的对象,优先
与最大的集合进行比较,这些都最大的回避了最坏情况。
(3)可能的改进:
首先可以实现将每个集合里面的字符串按照字典序进行排列,这样就可以将查找以及
合并的效率增高。
另外,可能采取恰当的数据结构也可以将查找以及合并等操作的效率得到提高。
题目:
三、编程题:30分 共1题
注意:要求提供完整代码,如果可以编译运行酌情加分。
1. 一条1百万节点的单向链表,链表所有节点是按value字段从小到大的顺序链接;下面是一个节点的结构
typedef struct node_t{
int value; /* 节点排序字段 */
int group; /* 组号: 0,1,2,3,4,5,6,7,8,9 */
struct node_t *pnext; /* 下个节点的指针 */
}node_t;
node_t head; /*该单向链表的头节点,全局变量 */
试设计程序:针对各个group(0-->9),根据value字段排序,输出各组top 10的节点。(top10是从小到大,取后面的大值top10.)
要求:尽量减少计算复杂度、遍历次数,不允许大量的辅助内存
code
#include <iostream>
#include<math.h>
#include<malloc.h>
using namespace std;
typedef struct node_t{
int value; /* 节点排序字段 */
int group; /* 组号: 0,1,2,3,4,5,6,7,8,9 */
struct node_t *pnext; /* 下个节点的指针 */
}node_t;
node_t *head; /*该单向链表的头节点,全局变量 */
void init()//初始化链表
{
int i;
node_t *p,*tmp;
p=(node_t *)malloc(sizeof(node_t));
head=NULL;
head=p;
p->pnext=NULL;
unsigned int k;
for(i=0;i<10000;i++)
{
tmp=(node_t *)malloc(sizeof(node_t));
k=rand()%10;
tmp->value=i;
tmp->group=k;
tmp->pnext=p->pnext;
p->pnext=tmp;
p=p->pnext;
}
}
//算法思想:设置一个10个长度为10的链表来存储group的0-9中每组的top10的value
//并设置含10个元素的数组b[10]作为计数器,统计每个链表的元素的个数,
//当元素个数多于10个时将第一个节点移到最后一个节点,并将取得的改组的值存在最后一个节点
//用含10个元素数组p[10]表示各个链表的表头
//首先遍历到链表的链尾
typedef struct node{
int value; /* 节点排序字段 */
struct node *pnext; /* 下个节点的指针 */
}node;
void sort()
{
node *a[10];
node *p[10],*tmp;//tmp作为临时存储器,使用它将表头节点移到表尾;
node_t *p1=head->pnext;//用p1存给的单链表
int i,b[10];
for(i=0;i<10;i++)b[i]=0;//初始化行计数器
for(i=0;i<10;i++)//为数组链表分配空间
{
a[i]=NULL;
tmp=(node*)malloc(sizeof(node));
a[i]=tmp;
p[i]=a[i];
a[i]->pnext=NULL;
}
while(p1!=NULL)//遍历链表
{
for(i=0;i<10;i++)
{
if((p1->group)==i)
{
if(b[i]<10)
{
b[i]++;
tmp=(node*)malloc(sizeof(node));
tmp->value=p1->value;
tmp->pnext=a[i]->pnext;
a[i]->pnext=tmp;
a[i]=a[i]->pnext;
}
else
{
tmp=p[i]->pnext;
p[i]->pnext=tmp->pnext;
tmp->value=p1->value;
tmp->pnext=a[i]->pnext;
a[i]->pnext=tmp;
a[i]=a[i]->pnext;
}
}//end if
}//end for
p1=p1->pnext;
}//end while
cout<<endl;
//打印*a[10]的值
for(i=0;i<10;i++)
{
cout<<" top "<<i<<":";
tmp=p[i]->pnext;
while(tmp!=NULL)
{
cout<<tmp->value<<" ";
tmp=tmp->pnext;
}
cout<<endl;
}
//释放内存
while(head!=NULL)
{
p1=head;
head=head->pnext;
free(p1);
}
for(i=0;i<10;i++)//释放10个数组
{
for(i=0;i<10;i++)
{
tmp=p[i];
p[i]=p[i]->pnext;
free(tmp);
}
}
}
int main()
{
init();
sort();
return 0;
}
计算机复杂度:N,遍历此数为N,辅助存储空间为100;这是一个满意的结果!
创建于: 2006-09-22 14:10:32,修改于: 2006-09-22 14:16:43,已浏览1076次,有评论8条
网友评论
网友:carleo 时间:2006-09-23 02:34:00 IP地址:219.239.227.★
while(p1!=NULL)
{
for(i=0;i<10;i++)
{
if((p1->group)==i)
...
用这样的结构岂不是对每个节点都在 for 循环中再进行 if 判断,跳转太多效率肯定大打折扣,
while(p1 != NULL) {
i = p1->group;
if ( i < 0 || i > 9)
continue;
if (b[i] < 10)
.....
else
......
.....
}
这样不就行了嘛
网友:xly 时间:2006-09-23 11:02:00 IP地址:202.198.31.★
没有理解对程序的意图,group值是0-9之间的一个随机数
这里的for(i=0;i<10;i++)
{
if((p1->group)==i)
相当于一个switch语句
只是让语句变得简练了,开销和switch是相同的
网友:本站网友 时间:2006-09-28 22:54:14 IP地址:60.216.184.★
程序写的错误真多
太垃圾了
网友:骂他两句 时间:2006-09-29 13:32:51 IP地址:219.133.5.★
这哪里是面试题,如果没有见过做过这样的题目,我不相信百度会有人当场写对。
网友:Merlin Ran 时间:2006-10-02 14:42:20 IP地址:125.213.91.★
怎么都用动态分配的数据结构啊。要充分利用链表有序的特点。直接用10个循环队列,每个都是10个元素的空间,从小到大放当前的top10。每个节点要是这个group,它肯定比原来的top10中的最大值还大,于是把这个值放到当前最大值的后一个,同时循环队列头往前挪一个。比较都不需要做。
扫描完了,数值里存的就是递增的top10了。
int top10[10][10]; // the current top 10 value of each group, impletemented by circular queue
int htop10[10]; // the head(next pos to write) of each circular queue
// to do: initialize each htop10 element to 0
node_t* p = head;
while (p=p->next) {
top10[p->group][htop10[p->group]] = value;
htop10[p->group] = (htop10[p->group]+1)%10;
}
for (int i = 0; i < 10; ++i) {
printf("Printing group %d...\n", i);
hprint = htop10[i];
do {
printf("%d\t", top10[i][hprint]);
hprint = (hprint+1)%10;
} while (hprint <> htop10[i]);
putc('\n');
}
忽略了每组值小于10个的情况。
网友:路人甲 时间:2006-10-05 19:13:59 IP地址:59.58.199.★
楼上正解
网友:本站网友 时间:2006-10-11 16:30:33 IP地址:125.33.175.★
typedef struct top10
{
char p;
node_t *link[10];
}top10;
findoutTop10(){
top10 t[10];
node_t *tnode;
tnode = head;
while(tnode!=null){
tnode=tnode.pnext;
t[tnode.group].link[t[tnode.group].p] = tnode;
t[tnode.group].p++;
if(t[tnode.group].p==10){
t[tnode.group].p==0;
}
}
}
网友:thinkingDeeply 时间:2006-10-13 18:09:59 IP地址:220.231.40.★
#include <stdlib.h>
#include <iostream.h>
#include <string.h>
typedef struct node_t {
int value;
int group;
struct node_t *pnext;
}node_t;
typedef struct queueNode {
int value;
struct queueNode *next;
}queueNode;
typedef struct queue_t {
queueNode *front;
queueNode *rear;
int size;
}queue_t;
void initQueueNode(queueNode *node,int value)
{
node->value = value;
node->next = NULL;
}
void initQueue(queue_t *q)
{
q->front = q->rear = NULL;
q->size = 0;
}
bool isFullQueue(queue_t *q)
{
return q->size == 10;
}
void destroyQueue(queue_t *q)
{
while(q->front)
{
queueNode *node = q->front;
q->front = q->front->next;
delete node;
}
q->front = q->rear = NULL;
q->size = 0;
delete q;
}
int pushQueue(queue_t *q,int value)
{
if(q->size == 10)
{
cout << "queue is full" << endl;
exit(1);
}
queueNode *node = new queueNode();
initQueueNode(node,value);
if(q->rear == NULL)
q->rear = node;
else
{
q->rear->next = node;
q->rear = q->rear->next;
}
if(q->front == NULL)
q->front = node;
q->size++;
return 1;
}
int popQueue(queue_t *q)
{
if(q->front == q->rear)
{
cout << "queue is null" << endl;
exit(1);
}
queueNode *node;
node = q->front;
q->front = q->front->next;
delete node;
q->size--;
return 1;
}
void init(node_t *p)
{
node_t *temp;
for(int i=0; i<1000000; i++)
{
temp = new node_t();
temp->value = i;
temp->group = rand()%10;
temp->pnext = NULL;
p->pnext = temp;
p = p->pnext;
}
}
int top[10][10],total[10];
node_t *head,*p;
node_t *temp;
int num;
queue_t *q[10];
void main()
{
for(int i=0; i<10; i++)
{
q[i] = new queue_t();
initQueue(q[i]);
}
p = new node_t();
head = p;
init(p);
p = head->pnext;
while(p)
{
num = total[p->group] == 10 ? 9 : total[p->group];
if(isFullQueue(q[p->group]))
{
popQueue(q[p->group]);
}
pushQueue(q[p->group],p->value);
total[p->group] = total[p->group] == 10 ? 10 : total[p->group]+1;
p = p->pnext;
}
for(i=0; i<10; i++)
{
cout << "Group " << i << endl;
q[i]->rear = q[i]->front;
while(q[i]->rear)
{
cout << q[i]->rear->value << " ";
if(q[i]->rear->next)
q[i]->rear = q[i]->rear->next;
else
break;
}
cout << endl;
}
p = head->pnext;
while(p)
{
temp = p;
p = p->pnext;
delete temp;
}
delete head;
for(i=0; i<10; i++)
{
destroyQueue(q[i]);
}
}
用队列来存储top10应该是最好的解决方案