微软十五道面试题
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,睡觉了