微软十五道面试题
1、有一个整数数组,请求出两两之差绝对值最小的值,
记住,只要得出最小值即可,不需要求出是哪两个数。
这个我没找到高效算法,O(n2)算法很好实现,应该都能想到的,另外一个快一点点的就是先排序一下,然后查找最小差的,排序用快排可以在)O(nlg(n))内完成,然后查找可以在线性时间完成,总复杂度O(nlg(n))
2、写一个函数,检查字符是否是整数,如果是,返回其整数值。
(或者:怎样只用4行代码编写出一个从字符串到长整形的函数?)
int char2int(char c){
if(c >= '0' || c<= '9')
return c - '0';
return -1;
}
3、给出一个函数来输出一个字符串的所有排列。
这个网上到处可以找到的
4、请编写实现malloc()内存分配函数功能一样的代码。 给出一个函数来复制两个字符串A和B。字符串A的后几个字节和字符串B的前几个字节重叠。
题目没看懂什么意思
5、怎样编写一个程序,把一个有序整数数组放到二叉树中?
排序的数组本身就可以当做是一颗排序二叉树,二分查找就是用这个特性的
struct tree_node{
int data;
struct tree_node* left;
struct tree_node* right;
};
struct tree_node* array2tree(int* array, int start, int end){
struct tree_node* ret;
int mid;
if(start > end)
return 0;
if(start == end){
ret = (struct tree_node*)malloc(sizeof(*ret));
ret->data = *(array + start);
ret->left = ret->right = 0;
return ret;
}
ret = (struct tree_node*)malloc(sizeof(*ret));
mid = (start + end) / 2;
ret->data = mid;
ret->left = array2tree(array, start, mid - 1);
ret->right = array2tree(array, mid + 1, end);
return ret;
}
6、怎样从顶部开始逐层打印二叉树结点数据?请编程。
这个就是经典的BFS了。
struct tree_node{
int data;
struct tree_node* left;
struct tree_node* right;
};
void BFS(struct tree_node* root){
queue<void*> bfs_queue;
struct tree_node* iter;
bfs_queue.push(root);
while(!bfs.empty()){
iter = (struct tree_node*)bfs_queue.pop();
if(!iter)
continue;
printf("%d", iter->data);
bfs_queue.push(iter->left);
bfs_queue.push(iter->right);
}
}
7、怎样把一个链表掉个顺序(也就是反序,注意链表的边界条件并考虑空链表)?
这个就保存连续的三个链表节点就可以反转单链表
struct list_node{
int test;
struct list_node* next;
};
struct list_node* reverse_list(struct list_node* head){
struct list_node* cur, *next, *nnext;
if(!head || !head->next)
return head;
cur = head;
next = head->next;
nnext = next->next;
while(!next){
next->next = cur;
cur = next;
next = nnext;
if(nnext)
nnext = nnext->next;
}
return cur;
}
8、请编写能直接实现int atoi(const char * pstr)函数功能的代码。
#define INT_MAX ~0x00
int atoi(char* pstr){
int sign = 0, ret = 0, index = 0, iter = 0;
int is_minus = 0;
int* array = 0;
if(!pstr || !strlen(pstr))
return INT_MAX;
array = (int*)malloc(strlen(pstr));
if(pstr[0] == '-'){
sign = 1;
is_minus = 1;
}else if(pstr[0] == '+')
sign = 1;
else{
int bit_int;
while((bit_int = char2int(pstr[index + sign])) != -1){
*(array + index) = bit_int;
index ++;
}
}
while(iter < index){
ret += array[iter];
ret *= 10;
}
if(is_minus)
ret = 0 - ret;
return ret;
}
9、编程实现两个正整数的除法
编程实现两个正整数的除法,当然不能用除法操作符。
// return x/y.
int div(const int x, const int y)
{
....
}
这个随便找本算法的书上就有了,就不写了
10、在排序数组中,找出给定数字的出现次数
比如 [1, 2, 2, 2, 3] 中2的出现次数是3次。
二分查找,然后分别向前和向后找相同的值就行了
11、平面上N个点,每两个点都确定一条直线,
求出斜率最大的那条直线所通过的两个点(斜率不存在的情况不考虑)。时间效率越高越好。
这个没找到高效算法,不善于处理这种问题
12、一个整数数列,元素取值可能是0~65535中的任意一个数,相同数值不会重复出现。0是例外,可以反复出现。
请设计一个算法,当你从该数列中随意选取5个数值,判断这5个数值是否连续相邻。
注意:
- 5个数值允许是乱序的。比如: 8 7 5 0 6
- 0可以通配任意数值。比如:8 7 5 0 6 中的0可以通配成9或者4
- 0可以多次出现。
- 复杂度如果是O(n2)则不得分。
这个就是类似正则表达式的匹配了,0当做通配符就可以了
13、设计一个算法,找出二叉树上任意两个结点的最近共同父结点。
复杂度如果是O(n2)则不得分。
从一个节点的parent的另外的一颗子树找另一个节点就可以了,二叉树上的时间复杂度为O(n)
struct tree_node{
int data;
struct tree_node* parent;
struct tree_node* left;
struct tree_node* right;
};
int BFS(struct tree_node* root, struct tree_node* find); //改进一下BFS,当在子树root中找到了find节点就返回1,否则返回0
int is_right(struct tree_node* node){
if(node->parent && node->parent->right == node)
return 1;
return 0;
}
int is_left(struct tree_node* node){
if(node->parent && node->parent->left == node)
return 1;
return 0;
}
struct tree_node* nearest_parent(struct tree_node* one, struct tree_node* two){
struct tree_node* iter;
if(BFS(one, two))
return one;
if(BFS(two, one))
return two;
iter = one;
while(1){
if(is_left(iter)){
if(iter->parent->right && BFS(iter->parent->right, two))
return iter->parent;
iter = iter->parent;
continue;
}
if(is_right(one)){
if(iter->parent->left && BFS(iter->parent->left, two))
return iter->parent;
iter = iter->parent;
continue;
}
return iter; //iter是root节点
}
}
14、一棵排序二叉树,令 f=(最大值+最小值)/2,
设计一个算法,找出距离f值最近、大于f值的结点。
复杂度如果是O(n2)则不得分。
排序二叉树上的最大值为树的最右边的节点,一直right下去就可以,最小值为最左边的节点,一直left下去就找到最小值,因此可以得到f。然后就在二叉树上找比f大的值且距离最小就可以了,这个复杂度应该为O(n)
float fvalue(struct tree_node* root){
float left_most = 0;
float right_most = 0;
struct tree_node* iter = root;
while(iter->left){
left_most = iter->data;
iter = iter->left;
}
iter = root;
while(iter->right){
right_most = iter->data;
iter = iter->right;
}
return (left_most + right_most) / 2;
}
struct tree_node* nearest(struct tree_node* root){
float f;
struct tree_node* sub_root, *temp;
float root_distance, sub_distance;
f = fvalue(root);
sub_root = root;
if(!sub_root)
return 0;
//root的data比f小,则所有的左子树都小,检查右子树就可以了
if(sub_root->data < f){
return nearest(sub_root->right);
}
//比较左子树和根节点的距离,选择较小的
root_distance = f - sub_root->data;
temp = nearest(sub_root->left);
if(!temp)
return sub_root;
sub_distance = f - temp->data;
if(root_distance > sub_distance)
return temp;
return sub_root;
}
15、一个整数数列,元素取值可能是1~N(N是一个较大的正整数)中的任意一个数,相同数值不会重复出现。
设计一个算法,找出数列中符合条件的数对的个数,满足数对中两数的和等于N+1。
复杂度最好是O(n),如果是O(n2)则不得分
32、一个文件中有40亿个整数,每个整数为四个字节,内存为1GB,写出一个算法:求出这个文件里的整数里不包含的一个整数
上面第一个题用位图算法或者用哈希表就可以了,由于N比较大,就选择位图了,所占内存小,两种方法的时间复杂度都为O(n)
第二个只能用位图了,算了一下大概占内存400M
struct bitmap{
unsigned int bits;
unsigned char map[1];
};
struct bitmap* alloc_bitmap(int size)
{
int aligned_size, total_size;
struct bitmap* ret;
if(size <= 0)
return 0;
aligned_size = (size + 8 * sizeof(char) - 1)>>(3 * sizeof(char)); //计算需要size位需要多少字节
total_size = sizeof(bitmap) + aligned_size - 1;
ret = (struct bitmap*)malloc(total_size);
if(!ret)
exit(0);
memset(ret, 0, total_size);
ret->bits = size;
return ret;
}
void free_bitmap(struct bitmap* map){
if(map)
free(map);
}
void clear_bit(struct bitmap* map, int index){
char c = 0;
int main = 0, minor = 0;
if(index >= map->bits)
return;
main = index >> (3 * sizeof(char)); //在map数组中的索引
minor = index % (8 * sizeof(char));//在字节中的索引
c = bitmap->map[main];
c = c & ~(0x01 << minor);
bitmap->map[main] = c;
}
void set_bit(struct bitmap* map, int index){
char c = 0;
int main = 0, minor = 0;
if(index >= map->bits)
return;
main = index >> (3 * sizeof(char));
minor = index % (8 * sizeof(char));
c = bitmap->map[main];
c = c | (0x01 << minor);
bitmap->map[main] = c;
}
int is_set(struct bitmap* map, int index){
char c = 0;
int main = 0, minor = 0;
if(index >= map->bits)
return 0;
main = index >> (3 * sizeof(char));
minor = index % (8 * sizeof(char));
c = bitmap->map[main];
return c & (0x01 << minor);
}
void find_pair(int* array, int num){
struct bitmap* map;
int iter = 0;
map = alloc_bitmap(num + 1); //由于是从1到N
while(iter < num){ //建立索引表
if(!is_set(map, array[iter]))
set_bit(map, array[iter]);
iter++;
}
iter = 0;
while(iter < num){ //在索引表中搜索相加为N+1的数对
if(is_set(map, array[iter]) && is_set(map, num + 1 - array[iter])){
printf("(%d, %d)\n", array[iter], num + 1 - array[iter]);
clear_bit(map, array[iter]);
clear_bit(map, num + 1 - array[iter]);
}
iter++;
}
}
下面是腾讯的一两题,随便写写吧
33、腾讯服务器每秒有2w个QQ号同时上线,找出5min内重新登入的qq号并打印出来。
struct qq_list_hashtable{
unsigned long qq;
unsigned long timestamp;
struct qq_list_hashtable *next, *prev;
struct qq_list_hashtable *hnext, *hprev;
};
struct hash_head{
struct qq_list_hashtable* next;
};
#define QQ_HASH_SIZE 11369 //最好使用素数,可以尽量均横的在哈希表上分布,避免某些地方碰撞次数特别多
struct hash_head[QQ_HASH_SIZE];
int hash_fn(int qq_num){
return qq_num % QQ_HASH_SIZE;
}
将qq号的链表组织在两个数据结构中,一个双链表当队列使用,保存5min进来的60万个qq号,并且记录加进来的时间戳,定时每秒从队列中删除存活时间大于5min的节点。同时将其组织在一个哈希表上,哈希表用溢出双链表实现,用于快速查找qq号是否在队列里面,也就是是否在5min内登录过的,没有登录则将其加入队列和哈希表。struct
hash_head就是哈希表的头,哈希函数就随便写了一个,常用的求余就可以了。这样既可以保证队列里面的qq最新,也可以快速查找。
代码没时间测试了,只是写出了思路,应该会有细节有错的,业余写算法的随便实现了几个,累的一B,睡觉了