这一周比较杂,笔试了网宿科技,上海先锋商泰,然后周四晚上面试了网宿科技,上来就出了道挺变态的求最大子矩阵和的题。
【上海先锋商泰】
笔试都是选择题,除开前面的基础题,后面个个都是完整的给程序进行“完型填空”,记得有道题是树和森林的构造和遍历、简单的四则混合(包括括号)的计算器、给不同区域的着色问题。
今天早上跑过去面试,老实说,上午的状态一般都烂的一笔,就随便出了道算法题让我做,大致的题意如下:
假如有一个很大的bit流,比如“10000100011100010010101...”,让你给出个算法统计bit流中的1的个数。
这个问题实际上可也转化成“统计一个Byte中"1”的个数”,或者可以参考这个哥们的做法,点这里。
【网宿科技】
网宿科技的笔试题大都是之前比较有名的一些笔试题,在网上大都可以搜到,不过第一题感觉挺容易让人糊涂的:
1、输出下列程序的结果,并进行相关的解释
#include<stdio.h>
int main()
{
char *str[]={"Welcome","To","Chinanet","Center"};
char **p=str+1;//p指向"To"的指针的指针
str[0]=(*p++)+2;//将"To"字符串的第3个字符的地址赋给str[0]
str[1]=*(p+1);//p指向"Chinanet"的指针的指针,将"Center"的地址赋给str[1]
str[2]=p[1]+3;//p[1]等价于*(p+1),即将"Center"字符串中的第3个字符地址赋给str[2]
str[3]=*(*(&str+1)-1)+1;//即将下一个指向该类str的指针解引用,
//减1得到"Center"的地址,解引用加1得到"enter"的地址赋给str[3]
printf("%s\n",str[0]);
printf("%s\n",str[1]);
printf("%s\n",str[2]);
printf("%s\n",str[3]);
getchar();
}
相应的在win7上运行vs2012得到如下结果:
2、1)设计一个宏 ISUNSIGNED_VAR(a):判断一个数是否是有符号数还是无符号数
2)设计一个宏ISUNSIGNED_TYPE(type):判断一个类型是有符号类型还是无符号类型
参考在《C专家编程》中的一个解决方案:
#define ISUNSIGNED_VAR(a) (a>=0 && ~a>=0)
#define ISUNSIGNED_TYPE(type) ((type)0-1>0)
当然,免不了要测试一下上面两个宏的可行性:
#include<stdio.h>
int main(void)
{
unsigned int a=1;
printf("variable \"a\" %s unsigned\n",ISUNSIGNED_VAR(a) ? "is":"isn't");
printf("type \"unsigned int\" %s unsigned\n",ISUNSIGNED_TYPE(unsigned int) ? "is":"isn't");
getchar();
return 0;
}
得到如下的运行结果:
事实上,对 unsigned char类型,上述宏会存在问题:具体可以参看:http://www.cnblogs.com/xkfz007/archive/2012/03/27/2420172.html
3、判断一个单向链表中是否存在循环。
这貌似是一道微软的笔试题,网上大致的思路是说用两个指针(一个快指针,一个慢指针)来遍历链表,如果两个指针能同时访问同一个结点就说明该单向链表中存在循环!
具体的代码实现如下:
typedef struct Node{
int data;
struct Node* next;
}Node,*List;
#include<stdlib.h>
bool isCircle(List L){
if(L==NULL || L->next==NULL)
return false; //链表为空,或者没有自环
if(L->next==L)
return true; //链表有自环
List pSlow=L;//每次一步
List pFast=L->next;//每次两步
for(;pSlow!=NULL && pFast!=NULL; pSlow=pSlow->next,pFast=pFast->next->next)
if(pSlow==pFast) return true; // 慢指针追上了快指针
return false; //存在结点的next指针为NULL,即不存在环
}
4、实现strstr函数:
函数原型:extern char* strstr(char* str1,char *str2)
功能:找出str2字符串在str1字符串中第一次出现的位置
返回值:返回该位置的指针,如找不到,返回空指针
这应该算是一道非常常规的字符串处理的笔试题,我的一种实现如下:
char *strstr1(char *str1,char *str2)
{
char *p,*q,*r;
if(!str1||!str2) return NULL;
for(p=str1;*p;p++){
for(q=str2,r=p;*r && *q;r++,q++)
if(*r!=*q) break;
if(*q==0) return p;
else if(*r==0) return NULL;
}
return NULL;
}
简单测试一下:
#include<stdio.h>
void main(){
char str1[]="my world!";
char str2[]="orl";
char * pos;
if((pos=strstr1(str1,str2))!=NULL)
printf("%s\n",pos);
else
printf("not found");
system("pause");
}
得到如下预期的运行结果:
之前看过一个相当NB的版本,摘录如下:
char* strstr(char* haystack,char* needle){
for(;;++haystack){
char* h=haystack;
for(char*n=needle;;++n,++h){
if(!*n) return haystack;
if(*h!=*n) break;
}
if(!*h) return NULL;
}
}
这里头还有个典故啊(干草堆中找针!)
5、输入一个整形数组,数组里有正数也有负数。编写程序,求出数组中连续多个数之和的最大值。要求时间复杂度为O(n)
//最大子序列和
int sum(int a[],int n){
//传入参数:数组地址和元素个数
int max=0,temp=0;
for(int i=0;i<n;i++){
temp+=a[i];
if(temp>max)
max=temp;
else if(temp<0)
temp=0;
}
return max;
}
#include<stdio.h>
#include<stdlib.h>
void main(){
int a[10]={-2, 11, -4, 13, -5, 2, -5, -3, 12, -9};
printf("最大子序列和为:%d\n",sum(a,10));
system("pause");
}
晚上面试的时候,网宿科技的面试官又给我出了一道和题4类似的题,只不过问题的主体已经换成了二维矩阵!
6、问题描述:给定一个M*N(0<M,N<=100)的矩阵,请找到此矩阵的一个子矩阵,并且此子矩阵的各个元素的和最大,输出这个最大的值。
Example:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
其中左上角的子矩阵:
9 2
-4 1
-1 8
此子矩阵的值为9+2+(-4)+1+(-1)+8=15
看吧,这与问题5是何其的像啊,只不过把一维的最大子段和换成了二维的最大子矩阵和,解决的方法实质应该还是一样的。
假设最大子矩阵的结果为从第r行到第k行,从第i列到j列的子矩阵,如下所示(假定数组各维下标都从1开始)
|a11 ... ... a1i ... ... a1j ... ... a1n|
|a21 ... ... a2i ... ... a2j ... ... a2n|
| . . . . . . . |
| . . . . . . . |
|ar1 ... ... ari ... .... arj ...... arn |
| . . . . . . . |
| . . . . . . . |
|ak1 ... ... aki ... ... akj ... ...akn |
| . . . . . . . |
|am1 ... ... ami ... ... amj ... ... amn|
那么,将从第r行到第k行的每一行中相同列加起来,可以得到一个一维数组如下:
(ar1+... ...+ak1, ar2+... ...+ak2, ... ..., arn+ ... ...+akn)
由此,我们可以将该问题转换成求该一维数组的最大子段和的问题了。
我给出的实现代码如下:
//求最大子矩阵和
int Max_in_Row(int a[],int n,int *begin, int *end){
//传入参数:数组首地址,数组元素个数
//传出参数:最大子序列的区间[begin,end]
int max=0,temp=0;
*begin=0,*end=0;
for(int i=0;i<n;i++){
temp+=a[i];
if(temp>max){
*end=i;
max=temp;
}
else if(temp<0){
temp=0;
*begin=i+1;
}
}
return max;
}
#define M 4
#define N 4
int MatrixCompressToRows(int a[M][N],int *rbegin,int *rend,int *cbegin,int *cend){
//输入参数:矩阵地址与行数M、列数N
//输出参数:最大子矩阵和开始与结束的行号列号
//打印出最大子矩阵及其和
int i,j,k;
int max=0,temp,temp1,temp2;
*rbegin=0,*rend=0,*cbegin=0,*cend=0;
int b[N];//将矩阵压缩成行,其每个元素保存从第i行开始到j行对应列的和
//memset(b,0,N);
for(i=0;i<M;i++){
for(k=0;k<N;k++) //b[k]的值在i循环中置零
b[k]=0;
for(j=i;j<M;j++){ //从第i行到第j行
for(k=0;k<N;k++)
b[k]+=a[j][k];//b[k]的值不用重新计算,只需每轮更新
temp=Max_in_Row(b,N,&temp1,&temp2);
if(temp>max){
max=temp;
*rbegin=i,*rend=j,*cbegin=temp1,*cend=temp2;
}
}
}
return max;
}
#include<stdlib.h>
#include<stdio.h>
void main(){
int a[M][N];
int rbegin,rend,cbegin,cend;
printf("请按行序输入矩阵a[%d][%d]中的各元素值:", M,N);
for(int i=0;i<M;i++)
for(int j=0;j<N;j++)
scanf("%d",&a[i][j]);
printf("最大子矩阵和为:%d\n",MatrixCompressToRows(a,&rbegin,&rend,&cbegin,&cend));
printf("最大子矩阵打印如下:\n");
for(int i=rbegin;i<=rend;i++){
for(int j=cbegin;j<=cend;j++)
printf("%d\t",a[i][j]);
printf("\n");
}
system("pause");
}
测试运行,得到如下的结果: