- 浏览: 69433 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
v_JULY_v:
v_JULY_v 写道晕死,怎么照片全死了...各位,有需要的 ...
经典算法实现之一:教你一步一步用c语言实现sift算法、下 -
v_JULY_v:
晕死,怎么照片全死了...各位,有需要的话,还是看本人的CSD ...
经典算法实现之一:教你一步一步用c语言实现sift算法、下
永久勘误:微软面试100系列答案V0.4版[第41-60题答案]
作者:July、何海涛等网友
---------------------------
几点声明:
I、 此微软面试100题系列永久更新,答案永久勘误,永久优化。
随时,永远,欢迎,任何人,针对任何一题,提出自己的思路、意见。
并对那些修正公布于博客上的答案的网友,表示最大的感谢。
II、 不管你愿不愿意相信或承认,这份微软等面试100题资料+答案系列,在整个网上,都是独一无二的,
且,它的的确确、真真实实的帮助了不下10万人。
任何人,在引用此份资料或答案,必须注明出处:http://blog.csdn.net/v_JULY_v
III、此份面试题系列暂仅限学习交流,任何组织、出版团体或个人不得私自据为己有,或侵权将其出版,违者必究。
向您的真诚,表示最大的敬意。谢谢。
全部资源,下载地址:
http://v_july_v.download.csdn.net/
100题永久维护(众网友,思路回复)地址:
http://topic.csdn.net/u/20101126/10/b4f12a00-6280-492f-b785-cb6835a63dc9.html
前40题的答案,请参考这里:
答案V0.2版[第1题-20题答案]
http://blog.csdn.net/v_JULY_v/archive/2011/01/10/6126406.aspx
答案V0.3版[第21-40题答案]
http://blog.csdn.net/v_JULY_v/archive/2011/01/10/6126444.aspx
---------------------------------------------------------------------------
40、求固晶机的晶元查找程序
晶元盘由数目不详的大小一样的晶元组成,晶元并不一定全布满晶元盘,
照相机每次这能匹配一个晶元,如匹配过,则拾取该晶元,
若匹配不过,照相机则按测好的晶元间距移到下一个位置。
求遍历晶元盘的算法 求思路。
关于第41题,请看以下网友的回复:
xiuxianshen
感觉很简单啊,你对应你的元件个数新建两个相同维数的一维数组,
一组保存检测的匹配情况,一组保存该元件的距离,二维数组也可以,
遍历前先考虑数据参数就可以了。
kicming
难就难在元件的分布情况是未知的 对机器来说 它是不知道边界的 它自己不知道换行
所以很难规定换行的条件 比如从左向右找 找到某个地方发现没有元件了
那是换行还是不换行呢
换行的话,右边可能还有元件,不换行,可能当前已经到晶元盘边界了 再往右找就是地板了..
所以想求一个“盲目”遍历算法。
41.请修改append函数,利用这个函数实现:
两个非降序链表的并集,1->2->3 和 2->3->5 并为 1->2->3->5
另外只能输出结果,不能修改两个链表的数据。
此题,合并链表,要求将俩个非有序排列的链表,有顺序的合并。
如下:
//引自一网友。
#include <stdio.h>
#include <malloc.h>
typedef struct lnode {
int data;
struct lnode *next;
}lnode,*linklist;
linklist creatlist(int m)//创建链表
{
linklist p,l,s;
int i;
p=l=(linklist)malloc(sizeof(lnode));
p->next=NULL;
printf("请输入链表中的一个数字:");
scanf("%d",&p->data);
for(i=2;i<=m;i++)
{
s=(linklist)malloc(sizeof(lnode));
s->next = NULL;
printf("请输入第%d个数字",i);
scanf("%d",&s->data);
p->next=s;
p=p->next;
}
printf("\n");
return l;
}
void print(linklist h)//打印链表
{
linklist p=h->next;
int t=1;
printf("打印各个数字:\n");
do
{
printf("请输出第%d个数:",t);
printf("%d\n",p->data);
p=p->next;
t++;
}while(p);
}
linklist mergelist(void)//两个链表合并
{
int e,n;
linklist pa,pb,pc,head;
printf("请输入第一个链表的长度:");
scanf("%d",&e);
pa=creatlist(e);
printf("请输入第二个链表的长度:");
scanf("%d",&n);
pb=creatlist(n);
head=pc=(linklist)malloc(sizeof(lnode));
pc->next=NULL;
while(pa&&pb)
{
if(pa->data<=pb->data)
{
pc->next=pa;
pc=pa;
pa=pa->next;
}
else
{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}
pc->next=pa?pa:pb;
return head;
}
void main()
{
linklist head;
head=mergelist();
print(head);
}
///////////////////////////////////
请输入第一个链表的长度:5
请输入链表中的一个数字:3
请输入第2个数字2
请输入第3个数字1
请输入第4个数字7
请输入第5个数字9
请输入第二个链表的长度:5
请输入链表中的一个数字:6
请输入第2个数字4
请输入第3个数字5
请输入第4个数字8
请输入第5个数字7
打印各个数字:
请输出第1个数:3
请输出第2个数:2
请输出第3个数:1
请输出第4个数:6
请输出第5个数:4
请输出第6个数:5
请输出第7个数:7
请输出第8个数:8
请输出第9个数:7
请输出第10个数:9
Press any key to continue
//引用yangsen600。
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
struct Node
{
int num;
Node * next;
};
Node * createTail()
{
int x;
Node *head = NULL, *p = NULL, *tail = NULL;
puts("\nplease enter some digits(end of '.'):");
while( 1 == scanf("%d",&x) )
{
p = (Node *)malloc(sizeof(Node));
p->num = x;
p->next = NULL;
if( NULL == head )
{
tail = p;
head = tail;
}
else
{
tail->next = p;
tail = p;
}
}
getchar();
return head;
}
Node * CombinationNode(Node* head1, Node* head2)
{
Node *head,*tail,*p = head1,*q = head2,*s;
if( NULL == p )
return q;
if( NULL == q )
return p;
tail = p;
if( p->num > q->num)
tail = q;
head = tail;
while( NULL != p && NULL != q )
{
if(p->num <= q->num )
//如果p所指元素<q所指元素,那么把p所指元素,率先拉入合并后的链表中,
//p赋给s,并从p的下一个元素p->next查找。
//直到发现p所指 不再 < q,而是p > q了 即转至下述代码的else部分。
{
s = p;
p = p->next;
}
else
{
s = q;
q = q->next;
}
tail->next = s;
tail = s;
}
if( NULL == p )
p = q;
s = p;
tail->next = s;
return head;
}
void printHead(Node *head)
{
if( NULL == head )
return;
printf("List: ");
while(head)
{
printf("%d->",head->num);
head = head->next;
}
puts("NUL");
}
void main( void )
{
Node* head1,*head2,*head;
head1 = createTail();
printHead(head1);
head2 = createTail();
printHead(head2);
head = CombinationNode(head1,head2);
printHead(head);
}
//////////////////////////////////////////
please enter some digits(end of '.'):
3 2 1 7 9.
List: 3->2->1->7->9->NUL
please enter some digits(end of '.'):
6 4 5 8 7.
List: 6->4->5->8->7->NUL
List: 3->2->1->6->4->5->7->8->7->9->NUL
Press any key to continue
//与上述那段,输出结果一致。
已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序。
//非递归实现 链表合并排序:
Node * Merge(Node *head1 , Node *head2)
{
if ( head1 == NULL)
return head2 ;
if ( head2 == NULL)
return head1 ;
Node *head = NULL ;
Node *p1 = NULL;
Node *p2 = NULL;
if ( head1->data < head2->data )
{
head = head1 ;
p1 = head1->next;
p2 = head2 ;
}
else
{
head = head2 ;
p2 = head2->next ;
p1 = head1 ;
}
Node *pcurrent = head ;
while ( p1 != NULL && p2 != NULL)
{
if ( p1->data <= p2->data )
{
pcurrent->next = p1 ;
pcurrent = p1 ;
p1 = p1->next ;
}
else
{
pcurrent->next = p2 ;
pcurrent = p2 ;
p2 = p2->next ;
}
}
if ( p1 != NULL )
pcurrent->next = p1 ;
if ( p2 != NULL )
pcurrent->next = p2 ;
return head ;
}
//递归实现,
Node * MergeRecursive(Node *head1 , Node *head2)
{
if ( head1 == NULL )
return head2 ;
if ( head2 == NULL)
return head1 ;
Node *head = NULL ;
if ( head1->data < head2->data )
{
head = head1 ;
head->next = MergeRecursive(head1->next,head2);
}
else
{
head = head2 ;
head->next = MergeRecursive(head1,head2->next);
}
return head ;
}
不放比较一下,这俩段核心代码,注意其区别:
Node * CombinationNode(Node* head1, Node* head2)
{
Node *head,*tail,*p = head1,*q = head2,*s;
if( NULL == p )
return q;
if( NULL == q )
return p;
tail = p;
if( p->num > q->num)
tail = q;
head = tail;
while( NULL != p && NULL != q )
{
if(p->num <= q->num )
{
s = p; //3.4
p = p->next; //
}
else
{
s = q;
q = q->next;
}
tail->next = s;
tail = s;
}
if( NULL == p )
p = q;
s = p;
tail->next = s;
return head;
}
和这段:
linklist mergelist(void)//两个链表合并
{
int e,n;
linklist pa,pb,pc,head;
printf("请输入第一个链表的长度:");
scanf("%d",&e);
pa=creatlist(e);
printf("请输入第二个链表的长度:");
scanf("%d",&n);
pb=creatlist(n);
head=pc=(linklist)malloc(sizeof(lnode)); //1.这
pc->next=NULL; //2.这
while(pa&&pb)
{
if(pa->data<=pb->data)
{
pc->next=pa; //3.这
pc=pa;
pa=pa->next;
}
else
{
pc->next=pb; //4.这
pc=pb;
pb=pb->next;
}
}
pc->next=pa?pa:pb;
return head;
}
再比较下,这俩段:
linklist mergelist(void)//两个链表合并
{
int e,n;
linklist pa,pb,pc,head;
printf("请输入第一个链表的长度:");
scanf("%d",&e);
pa=creatlist(e);
printf("请输入第二个链表的长度:");
scanf("%d",&n);
pb=creatlist(n);
head=pc=(linklist)malloc(sizeof(lnode));
pc->next=NULL;
while(pa&&pb)
{
if(pa->data<=pb->data)
{
pc->next=pa; //3
pc=pa; //1
pa=pa->next; //2
}
else
{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}
pc->next=pa?pa:pb;
return head;
}
和
//递归实现,
Node * MergeRecursive(Node *head1 , Node *head2)
{
if ( head1 == NULL )
return head2 ;
if ( head2 == NULL)
return head1 ;
Node *head = NULL ;
if ( head1->data < head2->data )
{
head = head1 ;
head->next = MergeRecursive(head1->next,head2);
}
else
{
head = head2 ;
head->next = MergeRecursive(head1,head2->next);
}
return head ;
}
相当于,
if ( head1->data < head2->data )
{
head = head1 ; //1.head=head1;
head->next = MergeRecursive(head1->next,head2); //2.head1=head1->next;
//3.head->next=head1
}
else
{
head = head2 ;
head->next = MergeRecursive(head1,head2->next);
}
return head ;
聪明的你,相信,不要我过多解释。:)。
第43题:
43.递归和非递归俩种方法实现二叉树的前序遍历。
咱们先来复习下,基础知识。
因为关于树的遍历,在此100题中已出现过太多次了。
二叉树结点存储的数据结构:
typedef char datatype;
typedef struct node
{
datatype data;
struct node* lchild,*rchild;
} bintnode;
typedef bintnode* bintree;
bintree root;
1.树的前序遍历即:
按根 左 右 的顺序,依次
前序遍历根结点->前序遍历左子树->前序遍历右子树
前序遍历,递归算法
void preorder(bintree t)
//注,bintree为一指向二叉树根结点的指针
{
if(t)
{
printf("%c",t->data);
preorder(t->lchild);
preorder(t->rchild);
}
}
然后,依葫芦画瓢,得到....
2.中序遍历,递归算法
void preorder(bintree t)
{
if(t)
{
inorder(t->lchild);
printf("%c",t->data);
inorder(t->rchild);
}
}
3.后序遍历,递归算法
void preorder(bintree t)
{
if(t)
{
postorder(t->lchild);
postorder(t->rchild);
printf("%c",t->data);
}
}
二叉树的创建方法,
void createbintree(bintree* t)
{
char ch;
if( (ch=getchar())==' ')
*t=NULL;
else
{
*t=(bintnode*) malloc(sizeof(bintnode));
(*t)->data=ch;
createbintree(&(*t)->lchild);
createbintree(&(*t)->rchild);
}
}
接下来,
咱们在讨论二叉树遍历算法的非递归实现之前,
先看一个顺序栈的定义及其部分操作的实现
typedef struct stack
{
bintree data[100];
int tag[100];
int top;
}seqstack;
void push(seqstack* s,bintree t)
{
s->data[s->top]=t;
s->top++;
}
bintree pop(seqstack* s) //出栈
{
if(s->top!=0)
{
s->top--;
return (s->data[s->top]);
}
else
return NULL;
}
好了,现在,我们可以看二叉树前序遍历的非递归实现了。
按照二叉树前序遍历的定义,无论是访问整棵树还是其子树,均应该遵循先访问根结点,
然后访问根结点的左子树,最后访问根结点的右子树的。
因为对于一棵树(子树)t,如果t非空,访问完t的根结点值后,就应该进入t的左子树,
但此时必须将t保存起来,以便访问完其左子树后,进入其右子树的访问。
yeah,就是这个意思。:)...
即在t处设置一个回溯点,并将该回溯点进栈保存。
在整个二叉树前序遍历的过程中,程序始终要做的工作分成俩个部分:
1.当前正在处理的树(子树)
2.保存在栈中等待处理的部分。
//注:当栈中元素位于栈顶即将出栈时,意味着其根结点和左子树已访问完成,
//出栈后,进入其右子树进行访问,
//前序遍历,非递归实现
void preorderT(bintree t)
{
seqstack s;
s.top=0;
while( (t)||(s.top!=0) ) //当前处理的子树不为空或栈不为空
{
while(t) //子树不为空
{
printf("%c",t->data); //1.先访问根结点
push(&s,t); //2.访问左子树之前,记得先把根结点进栈保存
t=t->lchild; //3.然后才访问左子树,
}
if(s.top>0) //栈不为空
{
t.pop(&s); //4.pop根结点
t=t->rchild; //5.访问右子树
}
}
}
//中序遍历,非递归实现,
void inorderT(bintree t)
{
seqstack s;
s.top=0;
while( (t)||(s.top!=0) ) //当前处理的子树不为空或栈不为空
{
while(t) //子树不为空
{
push(&s,t); //1.访问左子树之前,记得先把根结点push进栈
t=t->lchild; //2.访问左子树
}
if(s.top!=0) //栈不为空
{
t.pop(&s); //3.pop根结点(访问完左子树后)
printf("%c",t->data); //4.访问根结点 (把先前保存的t给拿出来,要用了..)
t=t->rchild; //5.访问右子树
}
}
}
//后序遍历,非递归实现
后序遍历的非递归算法,稍微复杂点。请看,
按照二叉树后序遍历的定义,无论是访问整棵树还是起子树,
均应该遵循先访问根结点左子树,然后访问根结点的右子树,最后访问根结点。
值得注意的是,当一个元素位于栈顶即将处理的是,其左子树的访问一定完成,
如果其右子树不为空,接下来应该进入其右子树尽情访问。
//注意了,
但此时该栈顶元素时不能出栈的,因为它作为根结点,其本身的值还未被访问。
只有等到其右子树也访问完成后,该栈顶元素才能出栈,并输出它的值。
因此,在二叉树后序遍历的算法中,必须使用seqstack类型的数组tag,
其每个元素取值为0或1,用于标识栈中每个元素的状态。
1.当一个元素刚进栈时,其对应的tag值置为0;
2.当它位于栈顶即将被处理时,其tag值为0.意味着应该访问其右子树。
于是将右子树作为当前处理的对象,此时该栈顶元素仍应该保留在栈中。
并将其对应的tag值改为1.
3.当其右子树访问完成后,该元素又一次位于栈顶,而此时其tag值为1,
意味着其右子树已访问完成,接下来,应该直接访问的就是它,将其出栈。
void postorderT(bintree t)
{
seqstack s;
s.top=0;
while( (t)||(s.top!=0) )
{
while(t)
{
s.data[s.top]=t;
s.tag[s.top]=0; //tag置为0
s.top++;
t=t->lchild; //访问左子树
}
while( (s.top>0)&&(s.tag[s.top-1]==1) )
{
s.top--;
t=s.data[s.top];
printf("%c",t->data);
}
if(s.top>0)
{
t=s.data[s.top-1];
s.tag[s.top-1]=1;
t=t->rchild;
}
else
t=NULL;
}
}
至此,完。
44.腾讯面试题:
1.设计一个魔方(六面)的程序。
2.有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。
请用5分钟时间,找出重复出现最多的前10条。
3.收藏了1万条url,现在给你一条url,如何找出相似的url。(面试官不解释何为相似)。
关于这题,请看众网友们给的思路或解答:
beingstudio
1、设计一个魔方(六面)的程序。
自我感觉用三维坐标描述每一个小块,对面提供旋转方法,然后每做一个变更就检测是不是成功了
2、有一千万条短信,有重复,以文本文件的形式保存,一行一条,
有重复。请用5分钟时间,找出重复出现最多的前10条。
如果是有序的
读进来就能出结果;
如果是无序的
建议采用hash或者双hash归类,如果想一次完成,还可以维护一个文件排列表
3、收藏了1万条url,现在给你一条url,如何找出相似的url。(面试官不解释何为相似)
例如 http://topic.csdn.net/u/20081029/22/c8fe34c1-25ab-4b94-986e-4c2fd4caa664.html
可以认为http://topic.csdn.net/u/20081029/22/是相似的
也就是说,我们可以认为url / 为相似的,因为一般对内容归类也会产生url前面的不同,所以 如果采用二
题的hash算法,可以稍作修改就可
jia_xiaoxin
1、设计一个魔方(六面)的程序。
可以用一个二维数组存储魔方的面,以及每一个面上的方块。
2、有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。
请用5分钟时间,找出重复出现最多的前10条。
首先我们将文本导入数据库,使用Having子句来实现这样的功能,我们利用如下语句
select count(*) ccount from table1
group by a1 having count(*)>1
order by ccount desc
这样得到的第一个记录就是出现重复次数最多的那组数字。
yangzhongwei1031
个人觉得第二题其实完全可以导入到数据库中,然后用sql就很容易把结果查出来了。至于说一千万条查询
速度很慢的问题,这个完全是可以优化的,这也正好考察了你数据库的知识。关键是看思路,不应该把问题想死了。
第三题找相似的url,什么是相似的既然没有讲,我觉得也可以用sql来实现,把数据导入到数据库中,我们
只要按这个字排序就可以了,字符串的排序大家都知道,相同的肯定是在一起的。这样可以从首字母开始
保证最大限度的相似。
我也刚入行不久,个人想法,也许我的方法不正确,只是觉得程序员这行编码能力固然重要,
但是想法思维也要活跃些,要懂得用不同的方法去解决问题,不然真的是除了coding还是coding了。
ck4918
1,把魔方展开,得到六个正方形,定义六个结构体,内容为一个9个点和一个编号,每个点包括一个颜色标示;
在魔方展开图中根据正方形的相邻关系编号,每个正方形都有四个函数:左翻、右翻、上翻、下翻。
根据相邻关系,每个操作会引起相邻面的相关操作;比如一个面的左翻会调用右边相邻面的左翻;也就
意味着左相邻面的0、1、2三个元素与当前面互换;递归下去,直到所有面都交换完毕;
2,建立一个红黑树a;
遍历短信,对每条短信取MD5值,对每个MD5值在a中做操作:如果有值,这个key对应的值就+1,否则就=1;
遍历完后对红黑树取值最大的10个数,复杂度为10lg n
3.正则表达式,呵
zhenming_liu
1、设计一个魔方(六面)的程序。
这个就是一点简单的线性代数了。
2、有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。
请用5分钟时间,找出重复出现最多的前10条。
同学最近写了一篇论文,好像是解这个的
http://www.cse.ust.hk/~qinzhang/papers/fp337-zhang.pdf
他的模型好像比问题复杂,但是即便是最简单的streamming模型也是这十年才有人做的。
3、收藏了1万条url,现在给你一条url,如何找出相似的url。(面试官不解释何为相似)
这些问题来来去去就是Hashing啦。如果是Hamming distance, 应该是能建造Locality sensitive hashing的(http://en.wikipedia.org/wiki/Locality_sensitive_hashing),
如果是edit distance的话,应该还是没有人建构的出来对应的Hash Function
(其实这也是把edit distance的测度空间嵌入到L_1的测度空间,我印象中好像是做不来的).
elovenana
1、设计一个魔方(六面)的程序。
typedef struct color
{
int r,g,b;
} color;
typedef struct omf
{
int 面[6],
color cl;
}mf;
2、有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。
请用5分钟时间,找出重复出现最多的前10条。
这个可以,先取出第一条,然后,存入变量并将此条删除,与下面的比较,遇到相同的删除,
并且,计数器加一,然后,写入到另一个文件,标题和次数;重复之;直到清空文件;
然后,去比较另一个文件的次数,即可;
3、收藏了1万条url,现在给你一条url,如何找出相似的url。(面试官不解释何为相似)
现在有许多的库文件都支持,正则表达式,只要用正则去匹配就可以了;
yinghan2005
2、有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。
请用5分钟时间,找出重复出现最多的前10条。
首先我们将文本导入数据库,使用Having子句来实现这样的功能,我们利用如下语句 select count(*)
ccount from table1 group by a1 having count(*)>1 order by ccount desc
这样得到的第一个记录就是
出现重复…
导入数据库很费时间的呀,5分钟绝对不够。
第二个问题,shell解决,不知道效率如何,
sort messages.txt |uniq -c |sort -k1 |tail -10
----------------------------------
更多,请参考:
http://topic.csdn.net/u/20081029/22/c8fe34c1-25ab-4b94-986e-4c2fd4caa664.html?11622
http://blog.csdn.net/lijiaz5033/archive/2008/11/05/3226574.aspx
还是第44题:
下文作者,lijiaz5033
第一题魔方
其实也很简单!
先说面,每面有四边,因此要建立4个句柄对应:上下左右,四个去面,就像双向链表节点有上下两面一样,
然后面里有方块矩阵,2级的数组,加完数组写个方法函数,叫旋转,参数是行列号/旋向 ,
旋向上下时行列号为行号,旋向左右时行列号为列号,意思是把某行某列往某方向旋转。
矩阵里有方块,方块有 创建六个面,按照魔方的样子,将第一面为正面,往下是底面(把第二面拿过来),
底面往下是背面,背面往下联就是上面,上面往下是正面,现在回到正面,正面往左联就是左面,
左面往左联就是后面,后面往左就是右面,右面往左是正面。。。。。。(这里不用罗索了,自己看明白了就知道怎么做了)
六个面创建完并上下左右连通后,这个程序就完成了
第2小题:
首先,一千万条短信按现在的短信长度将不会超过700M(平均情况下应该是350M),
使用内存映射文件比较合适.可以一次映射(当然如果更大的数据量的话,可以采用分段映射),
由于不需要频繁使用文件I/O和频繁分配小内存,这将大大提高了数据的加载速度.
其次,对每条短信的第i(i从0到70)个字母按ASCII码进行分组,
其实也就是创建树.i是树的深度,也是短信第i个字母.
//树结点定义
struct TNode
{
BYTE* pText;//直接指向文件映射的内存地址,使用BYTE而不用char是为符号问题
DWORD dwCount;//计算器,记录此结点的相同短信数
TNode* ChildNodes[256]; //子结点数据,由于一个字母的ASCII值不可能超过256,所以子结点也不可能超过256
TNode()
{
//初始化成员
}
~TNode()
{
//释放资源
}
};
//BYTE* pText直接指向文件映射的内存地址,使用BYTE而不用char是为符号问题
//int nIndex是字母下标
void CreateChildNode(TNode* pNode, const BYTE* pText, int nIndex)
{
if(pNode->ChildNodes[pText[nIndex]] == NULL)
{//如果不存在此子结点,就创建.TNode构造函数应该有初始化代码
//为了处理方便,这里也可以在创建的同时把此结点加到一个数组中.
pNode->ChildNodes[pText[nIndex]] = new TNode;
}
if(pText[nIndex+1] == '\0')
{//此短信已完成,记数器加1,并保存此短信内容
pNode->ChildNodes[pText[nIndex]]->dwCount++;
pNode->ChildNodes[pText[nIndex]]->pText = pText;
}
else //if(pText[nIndex] != '\0')
{//如果还未结束,就创建下一级结点
CreateNode(pNode->ChildNodes[pText[nIndex]], pText, nIndex+1);
}
}
//创建根结点,pTexts是短信数组,dwCount是短信数量(这里是一千万)
void CreateRootNode(const BYTE** pTexts, DWORD dwCount)
{
TNode RootNode;
for(DWORD dwIndex=0;dwIndex <dwCount;dwIndex++)
{
CreateNode(&RootNode, pTexts[dwIndex], 0);
}
//所有结点按dwCount的值进行排序
//代码略...
//取前10个结点,显示结果
//代码略...
}
这样处理看起来很复杂,其实就是为了减少比较次数.我认为大家看了这小段代码应该可以明白我的意思了,
其它的不多说了.
最后,就是对这些结点按dwCount的值进行排序,取前面的前10个结点就可以了.
我认为这问题只要是解决两方面的内容,一是内容加载,二是短信内容比较.
采用文件内存映射技术可以解决内容加载的性能问题(不仅仅不需要调用文件I/O函数,
而且也不需要每读出一条短信都要分配一小块内存),而使用树技术可以有效减少比较的次数.
当然基本思路是这样,如果有心情还可以在这基础上做一些优化处理,效果一定不会差的。
第3小题,略。
再看,第2小题:
2、有一千万条短信,有重复,以文本文件的形式保存,一行一条,有重复。
请用5分钟时间,找出重复出现最多的前10条。
iwillalwaysloveyou
1.短信长度是有限的,例如在中国短信长度范围为0-140字节,
2.题目中没有提到内存限制,假设内存是足够的(本题按下面算法最坏情况下需要1个多G)
2.建立140个元素的multimap数组(空短信可另行特殊处理),
下标为i的multimap与长度为i的字符串相对应。键为字符串的hash,值为字符串及其出现次数.
3.遍历短信,将短信根据长度进行处理,怎么处理我就不细说了
4.对每一个multimap,按字符串的出现次数,找出前10个字符串(也可能不足10个),
(可以用堆排序,复杂度为O(n*logn))
5.在4找出的所有字符串组成的集合中,按字符串的出现次数,找出出现最多的前10个
此题题目好像有点儿问题,次数最多的有可能不是刚好10个,例如有9个字符串各出现10次,有2个字符串出现9次,其他均小于9次。
July个人认为,建立R-B树,挺好的,如ck4918所说,
2,建立一个红黑树a;遍历短信,对每条短信取MD5值,对每个MD5值在a中做操作:
如果有值,这个key对应的值就+1,否则就=1;遍历完后对红黑树取值最大的10个数,复杂度为10*lgn。
---------------------------------------------------------------------------------------
接下来,请一次性,看第45-48题:
//注,此第45-48题的答案,仅仅只作为你思路的参考。
为了表示简明,以下我引用网友答案时,1、2、3、4、5即与以下的第45-48题,一一对应:.
雅虎:
1、对于一个整数矩阵,存在一种运算,对矩阵中任意元素加一时,需要其相邻(上下左右)
某一个元素也加一,现给出一正数矩阵,判断其是否能够由一个全零矩阵经过上述运算得到。
2、.一个整数数组,长度为n,将其分为m份,使各份的和相等,求m的最大值
比如{3,2,4,3,6} 可以分成{3,2,4,3,6} m=1;
{3,6}{2,4,3} m=2
{3,3}{2,4}{6} m=3 所以m的最大值为3
3、.搜狐:
4对括号可以有多少种匹配排列方式?比如两对括号可以有两种:()()和(())
4、创新工场:
求一个数组的最长递减子序列 比如{9,4,3,2,5,4,3,2}的
最长递减子序列为{9,5,4,3,2}
5、微软:
一个数组是由一个递减数列左移若干位形成的,比如{4,3,2,1,6,5}
是由{6,5,4,3,2,1}左移两位形成的,在这种数组中查找某一个数。
在此,只重点给出第4题,最长递减子序列的答案:
4、创新工场:最长递减子序列
求一个数组的最长递减子序列 比如{9,4,3,2,5,4,3,2}的
最长递减子序列为{9,5,4,3,2}
======================================================================
关于这5道题,看网友们给的回复[仅供参考]:
fire_woods
1. 有2个弱判断准则,不过不知道3个加起来是不是和题目等价
1) 讲矩阵分成黑白棋盘格, 所有黑色格子的数字和等于白色格子的.
2)对任意一个位置, 他的值不大于周围(上下左右)4个临格的数值的和.
2. 背包问题, 当然有些条件可以预先判断,比如(所有元素的和%m)!=0, 那么就不需要处理了.
3.1*1+3*3+2*2=14
4.直接遍历, O(n)
5.二分法查找分界点, 再二分法查找O(ln(n))。
duguyue200
1.第一反应是广搜,因为以前遇过一个这样类似的问题,应该是变式之类的。。
2.第一反应有数学方法,结合爆搜吧。一定是可以解的不过搜索量不小,可能用深搜好点。
3.第一反应是排列组合。因为规则给的很多。
4.第一反应就是动归,没说的,经典问题。
5.第一反应是因为有局部的递减性质,那就找到断点,不要恢复(因为恐怕有阴人的地方,不是一次能够恢复得了的)。局部用二分查找。
wds530405973
第2题 算法 原理的思想是将大问题转换成小问题。
就{3,2,4,3,6}的操作步骤:
第一步:想将数组递减排序得{6,4,3,3,2},求出数组中所有数的和m=18,第一个最大的数b=6, m/b=3余数为0,当除数为1,余数为0时终止。当余数不为0时,转到第三步。当余数为0时将数组划分为{6},{4,3,3,2}两个。把{4,3,3,2}看成一个新的数组。
第二步:先用{4,3,3,2}中的最大数与b=6比较,即4<b,所以再将4与最右边的数即2相加与b比较,结果相等,则将这两个数从该数组中除去生成新的数组,转到第一步,现在的结果是{6},{4,2},{3,3},把{3,3}看成一个新的数组继续重复第二步。
第三步,将数组中最大的数与最小的数取出构成一个新数组Z,剩余的构成一个数组,然后,判断m/Z中数字之和看是否余数为0,若为0,把b替换为Z中数字之和转第二步,若不为0,继续从剩余的数字中取出最小值加入到Z中,再判断m/Z中数字之和看是否余数为0,直到为0,转第二步为止。
最后得到的结果是{6},{4,2},{3,3} 这时可以计算出m为3,也可以在程序中作记载。
在第二步工程过,若出现两个数相加大于上一次的b,则将程序转到第三步。
Beiyouyu
5. 二分解决。注意到a[0] <= a[n-1],设要查找的是key、那么a[mid]、key、a[0]、a[n-1]可以确定一个顺序,将原数组切分成两部分:1部分是纯递减数组、另一部分是原问题的子问题。
具体如下:
如果a[mid] <= a[0] :那么a[0...mid]是纯递减数组;可以简单比较出key是否在a[0..mid]中,如果在那么问题就是标准的二分查找了;否则,a[mid+1...n-1]就是原问题的子问题了。
如果a[mid] >= a[n-1]: 那么a[mid...n-1]也是纯递减数组;与上类似。
Zhifeidie
第1题思路:动态规划,递归
1、先判断矩阵中的每一个值,是否可以以其为中心减1,也就是这个值和它的上下左右都大于1,如果成立,则以此值为中心包括上下左右减1,对生成的新的矩阵递归计算。退出条件:全0表示成功,如果不是全0但是所有的值都不满足前面的条件,返回失败。
第2题思路:动态规划,递归
将整个数组作为一个集合,最大的可能值就是集合的大小了,最小肯定是1,那么从2开始一次判断。如果集合可被k等分,那么首先集合的和能够被k整除,如果这个条件满足,则重复k-1次从这个集合中取出和为sum/k的子集合。
取子集合的算法是一个递归的思想,详见153楼
其他几个题目都是比较经典的问题,不赘述。
============================
以下,直到出现下一条杠杠"==========="之前,写的都是第4题。
pmars
说一下第四题:
一道做过的ACM题:
想法是弄一个数组存放已经找到的最长子序列,当然里面的元素都是找到的在各个位置上的最大的值(记得以前我发过一个这个题目的帖子)
http://topic.csdn.net/u/20100424/22/7a7b4a50-9110-4cf8-96ec-7fa728593b15.html
能做到NlogN吧!
litaoye
二部图是什么?二分图?
这个问题就是一个最大递增序列问题,最普通的想法就是用DP,n^2肯定可以(类似于LCS问题)。当然还可以优化到n*log(n),那是另一回事儿了。
litaoye
代码网上一大堆,说说思路吧,以4 2 6 3 1 5为例
逐个读入数字,
4 | 此时可能的队列长度为1,最大值为4
4 2 | 由于2 < 4,此时队列长度为1,最大值为2
4 2 6 | 6 > 2,队列有2个,一个长度为1,最大为2,一个长度为2,最大为6
4 2 6 3 | 3 < 6, 3 > 2,队列有2个,一个长度为1,最大为2,一个长度为2,最大为3
4 2 6 3 1 | 1 < 2, 1 < 3,队列有2个,一个长度为1,最大为1,一个长度为2,最大为3
4 2 6 3 1 5 | 5 > 1,5 > 3,队列有3个,一个长度为1,最大为1,一个长度为2,最大为3,一个长度为3,最大为5(分别是1 | 2,3 | 2,3,5)
走到头了,所以输出3,此时是一个最坏情况下n^2的算法,但如果每读入一个新的数时,不是逐个比较,而是利用二分法,查到小于该数的最长序列,那么就是n*log(n)的方法了。
litaoye
还是贴一段简单的代码吧,给自己留个备份,临时写的,希望没有错,C#的。
using System;
namespace csdnTest
{
class Program
{
static void Main(string[] args)
{
int[] items = new int[] { 4, 2, 6, 3, 1, 2, 5 };
//用来记录序列长队最大值的数组,index + 1就是序列的长度
int[] maxValues = new int[items.Length];
int maxLength = 1;
maxValues[0] = items[0];
for (int i = 1; i < items.Length; i++)
{
//二分查找对应的最长序列
int lengthIndex = Array.BinarySearch<int>(maxValues, 0, maxLength, items[i]);
//针对于.Net里面的BinarySearch的特殊处理,不用理会
if (lengthIndex < 0)
lengthIndex = -lengthIndex - 1;
if (lengthIndex + 1 > maxLength)
maxLength = lengthIndex + 1;
maxValues[lengthIndex] = items[i];
}
Console.WriteLine(maxLength);
}
}
}
第4题完。
======================================================
thegodofwar
大公司都爱考些“动态规划”的题,因为“动态规划”解法不定,
可能用到多种其它经典的算法(比如递归、回溯、二分、修枝......)
higherone
第一题很多人都没理解对。题目中说的是元素加一的时候,相邻的四个元素的某一个必须加一(四个中的任一个都可以),而不是四个相邻元素都加一。
感觉2楼提出的2个弱判断准则还挺有道理的。给出代码的解答我都没看,还是愿意看解题思路,动不动给代码的,多半都没搞清楚问题。
--------------------
//这里说的2楼给的2个弱判断准则,是这:-------
fire_woods
有2个弱判断准则,不过不知道3个加起来是不是和题目等价
1) 讲矩阵分成黑白棋盘格, 所有黑色格子的数字和等于白色格子的.
2)对任意一个位置, 他的值不大于周围(上下左右)4个临格的数值的和.
----------------------------------------------------------
如果这两个准则不是充要条件,那么我只想到一个回溯的方法。从某个元素开始,尝试找一个他的邻居与他成为一组,共同减一。这算一步操作。如果这样减下去,最终能达到0矩阵,则有解;否则回溯到上一步,找其它邻居成为一组,共同减一。
但是这个回溯的方法在这里有很大的弊病:回溯次数不仅跟矩阵阶数有关,还跟矩阵元素值有关,元素值越大,就越多回溯次数。所以这个算法肯定是不靠谱的。
higherone
二分最大匹配,不是求匹配的最大数目么?怎么在这个题目中应用?
另外,本题中的匹配,还不能是任意两个黑白子的匹配,而是相邻的黑白子才能匹配。是不是用起来会有问题?
接上:
litaoye
每个黑点只跟周围相邻的白点联通,白点也只跟周围相邻的黑点联通,有一个共同的源S连到黑点,每条边的容量就是黑点上面的数字,黑点同白点之间的连线,容量都看作无穷大,所有白点都连到一个共同的汇点T,权值就是白点上面的数字,如果从源S到T之间的最大流=所有黑点上面的数字和 同时=所有白点上面的数字和,那么该矩阵就是可以被还原的,以上是最大流的解法,肯定可以得出正确的结果,但至于是否为最优方法,就不一定了,这题看起来用类似贪心的思路兴许也成,不过没证明过,也没仔细想过反例,最近针对于这类黑白染色的问题,我总犯错误,只好老老实实的用最大流了。
higherone
牛!
刚看了下最大流算法的资料,理解了一下,真的是可以解决问题的。
说下我的理解,大家指正:
用黑点连线,指向相邻的白点,模拟了黑点和白点组合在一起加一的情况,并设该连线容量无穷大,是说流量在这里不是瓶颈。
源点S的流量,通过黑点,再通过白点,最后到达汇点。“最大流量=黑点之和=白点之和”,实际上是说源点到黑点的流量,最终都流到白点到汇点的连线上了,也就是黑点的每个1都找到了一个白点与之组合。
因此这个条件就等价与原题目。
那么,刚才那个最大匹配的做法不靠谱了?
=======================
lanhaibin
微软:
5.一个数组是由一个递减数列左移若干位形成的,比如{4,3,2,1,6,5}是由{6,5,4,3,2,1}左移两位形成的,在这种数组中查找某一个数。
先将数组右移恢复为原来的递减数列,然后运用二分查找即可。
jinwen0915
第三题:12种
1 () () () ()
2 (()) () ()
3 () (()) ()
4 () () (())
5 () (() ())
6 (() () ())
7 ((()) ())
8 ((()) ())
9 (() ()) ()
10((())) ()
11(()) (())
12() ((()))
lmgsdu
关于第一题,感觉类似七桥问题。个人有个解法不知道对不对。
假设正数矩阵每一个元素都是大于0的。
求矩阵每一行与列的和,满足以下两个条件即可由全零矩阵得到:
1、如果行与列的和有为奇数的,那么必须分别为偶数个。
2、在所有和为奇数的行与列中,相邻的奇数和不能为偶数对(顶角处的奇数行与奇数列不算做相邻)
还原假设条件,如果矩阵中存在0,且能够将矩阵分割成几个由正整数组成的小矩阵,则对小矩阵套用以上两个条件即可。
个人想法。
第45题至第48题完。
由于这些题的思路,都是整理各个网友的,很乱,见谅。
至于是否正确,请自己辨明。
也希望,看到此份资源的答案,如果有更好的思路、解答,请务必与我联系。
=====================================================================
49.一道看上去很吓人的算法面试题:
如何对n个数进行排序,要求时间复杂度O(n),空间复杂度O(1)
此题请看下文,作者张羿:
看上去似乎任何已知的算法都无法做到,如果谁做到了,那么所有的排序方法:QuickSort,ShellSort,HeapSort,BubbleSort等等等等,都可以扔掉了,还要这些算法干吗阿,呵呵。不过实际上,在数字范围有限制的情况下,是有一个这样的算法的,只需要用一个数组记录每个数字出现次数就可以了。
假定你的数字范围在0到65535范围之内,定义一个数组count[65536](这个空间是常量,和n无关,所以是O(1) ),初值全部为0。
那么假设有下面这些数字:
100
200
300
119
0
6
...
那么对于每个这个数字,都做在count中记录一下:
100 => count[100]++
200 => count[200]++
300 => count[300]++
119 => count[119]++
0 => count[0]++
6 => count[6]++
...
最后,遍历一边所有这些数字就可得到0~65535每个数字的个数(在count数组中),然后再顺序遍历count数组,count[n] = m,则输出m个n,(比如说有count[3] = 2, 那么说明有2个数字3),依次输出,最后可得结果。第一次遍历是O(n),第二次遍历是O(1),为常量,所以最后的时间复杂度为O(n),而空间复杂度为O(1)
这个算法很简单,相信大家都会,只是这个题太过于变态了,一般会把面试者吓住(我原来面试也出过这个题,只不过题目的表述形式要“友善”的多,呵呵)
50.网易有道笔试:
1.求一个二叉树中任意两个节点间的最大距离,两个节点的距离的定义是 这两个节点间边的个数,比如某个孩子节点和父节点间的距离是1,和相邻兄弟节点间的距离是2,优化时间空间复杂度。
2.求一个有向连通图的割点,割点的定义是,如果除去此节点和与其相关的边,有向图不再连通,描述算法。
第1小题,就是本微软等面试100题系列,第11题。
//请参考答案V0.3版。上有我给的详细思路阐述。
I am very,sorry,此刻在整理答案时,我才发现,原来这第50题与本微软等100题系列第39题重复了。
非常抱歉。望众位见谅。
此题,请参考答案V0.3版(第20-40题的答案)。
----------------------------------------------------------------
以下的10题,第51题-60题答案参考,皆整理自网友何海涛博客。
何海涛CSDN主页:http://hi.csdn.net/cadcisdhht
51.和为n连续正数序列。
题目:输入一个正数n,输出所有和为n连续正数序列。
例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。
分析:这是网易的一道面试题。
这道题和本微软面试100题系列V0.1版的第14题有些类似。
我们可用两个数small和big分别表示序列的最小值和最大值。首先把small初始化为1,big初始化为2。
如果从small到big的序列的和大于n的话,我们向右移动small,相当于从序列中去掉较小的数字。
如果从small到big的序列的和小于n的话,我们向右移动big,相当于向序列中添加big的下一个数字。
一直到small等于(1+n)/2,因为序列至少要有两个数字。
基于这个思路,我们可以写出如下代码:
void PrintContinuousSequence(int small, int big);
// Find continuous sequence, whose sum is n
void FindContinuousSequence(int n)
{
if(n < 3)
return;
int small = 1;
int big = 2;
int middle = (1 + n) / 2;
int sum = small + big;
while(small < middle)
{
// we are lucky and find the sequence
if(sum == n)
PrintContinuousSequence(small, big);
// if the current sum is greater than n,
// move small forward
while(sum > n)
{
sum -= small;
small ++;
// we are lucky and find the sequence
if(sum == n)
PrintContinuousSequence(small, big);
}
// move big forward
big ++;
sum += big;
}
}
// Print continuous sequence between small and big
void PrintContinuousSequence(int small, int big)
{
for(int i = small; i <= big; ++ i)
printf("%d ", i);
printf("\n");
}
52.二元树的深度。
题目:输入一棵二元树的根结点,求该树的深度。
从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,
最长路径的长度为树的深度。
例如:输入二元树:
10
/ \
6 14
/ / \
4 12 16
输出该树的深度3。
二元树的结点定义如下:
struct SBinaryTreeNode // a node of the binary tree
{
int m_nValue; // value of node
SBinaryTreeNode *m_pLeft; // left child of node
SBinaryTreeNode *m_pRight; // right child of node
};
分析:这道题本质上还是考查二元树的遍历。
题目给出了一种树的深度的定义。当然,我们可以按照这种定义去得到树的所有路径,也就能得到最长路径以及它的长度。只是这种思路用来写程序有点麻烦。
我们还可以从另外一个角度来理解树的深度。如果一棵树只有一个结点,它的深度为1。
如果根结点只有左子树而没有右子树,那么树的深度应该是其左子树的深度加1;
同样如果根结点只有右子树而没有左子树,那么树的深度应该是其右子树的深度加1。
如果既有右
相关推荐
微软面试100道题系列是一套针对IT行业内应聘微软等知名科技公司的面试题集,它主要包括三个部分:数据结构、算法和海量数据处理。这些内容是程序员面试中非常重要的三个考察方向。 数据结构是计算机存储、组织数据...
### 数字通信(第四版)习题答案及勘误修正解析 #### 第二章知识点解析 **知识点一:概率计算** 在《数字通信(第四版)》第二章中,问题2.1涉及到基本的概率计算原理。该问题展示了如何计算两个事件集合\(A_i\)...
- **内容**:勘误表涉及多个章节和题目,包括但不限于计算题、选择题、证明题等不同类型的题目。 #### 重点勘误内容解析 ### 计算题与选择题勘误 - **3页17题**:原题指出该题的极限不存在,因此建议考生跳过此题...
本资料集包含了刘锦波教授编写的《电机与拖动》一书的习题答案,为学习者提供了清晰的解题思路和方法,同时附带了一份勘误文档,帮助读者避免因教材印刷错误而产生的理解困扰。 电机与拖动的知识点主要包括以下几个...
《C Primer Plus 第六版》是一本广受欢迎的C语言学习书籍,中文版的源代码、勘误和习题答案的提供,对于学习者来说无疑是一份宝贵的资源。以下将详细解析这些内容的价值和相关知识点。 首先,源代码是编程学习中的...
根据提供的文件信息,我们可以推断出这是一份关于2021年考研数学中的330题纠错(勘误)文档。这份文档旨在帮助考生纠正练习题中的错误,并且通过详细的解析来加深对数学概念的理解。下面将从几个方面详细阐述这份...
文章所引用的出版信息“Eur.Phys.J.C(2018)78:747”表明,这篇文章发表在《欧洲物理期刊C辑》(European Physical Journal C)上,具体在第78卷第747页。《欧洲物理期刊C辑》是欧洲物理学会的旗舰期刊之一,专注于...
完整英文电子版IEEE Std 802.21-2017-Cor 1-2017 IEEE Standard for Local and metropolitan area networks--Part 21: Media Independent Services Framework--Corrigendum 1: Clarification of Parameter ...
- 第7页、第21页、第39页、第41页、第47页、第51页、第66页、第72页、第117页、第75-76页等也存在相应的勘误内容,涉及文字表述、图表错误、代码示例等方面的修正。 通过以上对《CLR via C#》第三版中文版勘误内容...
经典数据结构与算法分析in C书+课后题答案+书中源代码+勘误表数据结构与算法分析in C书+课后题答案+书中源代码+勘误表数据结构与算法分析in C书+课后题答案+书中源代码+勘误表数据结构与算法分析in C书+课后题答案+...
《算法导论》第二版是计算机科学领域的一本经典教材,由Thomas H. Cormen、Charles E. Leiserson、Ronald L. Rivest和Clifford Stein合著。这本书深入浅出地介绍了各种基础和高级算法,以及如何分析它们的效率。整理...
#### 一、网络规划设计师考试概览 - **目标人群**:面向准备参加网络规划设计师级别考试的专业人士。 - **内容覆盖**:包括但不限于网络架构设计、网络安全策略制定、网络设备配置与管理等方面的知识点。 #### 二...
除了理论学习与勘误资料,书中还包括了一系列模拟测试题,这些题目不仅是检验学生理论知识掌握程度的工具,也是提升解题技巧、加深理解的手段。模拟测试题通常包括矩阵的线性变换、行列式的计算、矩阵的秩和逆等重要...
标题“勘误:中微子选项中的脂肪生成”看起来是一个翻译错误或者OCR(光学字符识别)的识别错误,因为文中讨论的主题是关于中微子物理学中的 leptogenesis(轻子生成)和相关研究工作。在这个情况下,“脂肪生成”...
本文勘误内容主要涉及在物理学领域,特别是粒子物理学和高能物理学中,关于“Di-boson签名作为部分合成的标准蜡烛”研究的补充和修正。这部分内容主要出现在一篇文章的附录C中,其中包括特定符号和等式的修正。具体...
### 勘误:用于CRESST暗物质实验的基于Geant4的电磁背景模型 #### 背景介绍 暗物质是构成宇宙的重要组成部分之一,科学家们长期以来一直致力于寻找暗物质粒子存在的直接证据。CRESST(Cryogenic Rare Event Search...
【考研数学一复习指南:利用03-17年合工大模拟题突破备考难关】 作为全国硕士研究生统一招生考试的重要组成部分,考研数学一历来被理工科及部分经济管理类专业的学生视为备考中的“硬骨头”。考试内容涉及高等数学...
#### 四、MCS-51系列单片机的基本芯片及其差异 **1.6 基本芯片类型及其差异** MCS-51系列单片机的基本芯片主要有三种:8031、8051和8751。 - **8031**:内部包括一个8位CPU、128B RAM、21个特殊功能寄存器(SFR)、...
勘误表与课后习题答案(0224).doc