【面试题】面试题合集三
1.【百度】给定一个无序数组,其中有一个元素个数超过数组元素个数的一半,请找出该元素,要求时间复杂度为O(n),空间复杂度为O(1)。
2.【360】给定一个无序数组,寻找第k大的元素,要求时间复杂度为O(n)。
3.【XX】给定一个含有2*k+1个元素的无序数组,其中有k个元素出现两次,有1个元素出现一次,请找出该元素,要求时间复杂度为O(n)。
4.【XX】给定一个含有2*k+2个元素的无序数组,其中有k个元素出现两次,有2个元素出现一次,请找出该元素,要求时间复杂度为O(n)。
5.【XX】给定一个含有4*k+2个元素的无序数组,其中有k个元素出现四次,有1个元素出现两次,请找出该元素,要求时间复杂度为O(n)。
6.【XX】给定两个无序数组X[m],Y[n],求两个数组按递增形式合并后,位于中间的元素,要求时间复杂度为O(n)。
7.【群硕】编写一个程序判断一个数是否是回文素数(回文素数就是从左向右读与从右向左读完全一样的素数,比如11,101,131,151,313,...)
分析:主要是回文的判断,是否素数是很好判断的。
判断回文,最简单就是将数字翻转过来,然后判断是否相等即可。
#include <stdio.h> #include <stdlib.h> int is_sushu(int val){ int k; if(val<0||val%2==0) return -1; if(val==1||val==2) return 0; for(k=3; k<val/2; k+=2) if(val%k==0) return -1; return 0; } int is_huiwen(int val){ int temp=val; int k=0; while(temp){ k=k*10+temp%10; temp = temp/10; } if(k==val) return 0; else return -1; } int main(void){ int val; scanf("%d", &val); if(is_sushu(val)==0&&is_huiwen(val)==0) printf("%d is a huiwensushu\n", val); else printf("%d is not a huiwensushu\n", val); system("pause"); return 0; }
8.【群硕】输入两个整数n和m,从数列1,2,3,...,n中随意取几个数,使其和等于m,要求将其中所有的可能组合列出来。
题目木有说明是否可以重复取值,但假设可以取重复数,可知最多可以取m个数,m*1=m嘛,范围为[1, ∞)如果不可取重复数,那么,至少需要取k个数((1+k)*k/2≥m),而最大值则为(n+1)*n/2,所以取值范围为[1, (n+1)*n/2]。
①可以取重复数
那么最多有m层递归,但层数不确定,无法用循环,所以还是用递归里面的回溯法吧。
只要保证下一层递归取值为≥上一层递归的取值,则可以确保结果的不重复性,因为最多可以取m个数,那么分配m个存储空间保存结果即可。
find_two表示从1,...,n中取两个数a,b使得a+b=m,
那么当取定a=k时,再从1,...,n中寻找两个数a',b'使得a'+b'=m-k即可,这样就成为子问题了。
递归的结束条件则为当求和的值m<=0则直接结束,如果m≤n时,还可以找到一个数的情况,所以不要遗漏即可。
#include <stdio.h> #include <stdlib.h> static int *num; static int len; void find_two(int m, int n, int s){ int k; if(m<=0) return; if(m<=n&&m>=s){ for(k=0; k<len; k++){ printf("%d ", *(num+k)); } printf("%d", m); printf("\n"); } for(k=s; k<=n; k++){ if(k>m) break; *(num+len++)=k; find_two(m-k, n, k); len--; } } int main(void){ int m,n; scanf("%d", &n); scanf("%d", &m); num = (int*)calloc(m, sizeof(int)); len = 0; find_two(m, n, 1); system("pause"); return 0; }
②不可取重复数(原理与①相似,在①的代码上稍作修改即可)
#include <stdio.h> #include <stdlib.h> static int *num; static int len; void find_two(int m, int n, int s){ int k; if(m<=0) return; if(m<=n&&m>s){ for(k=0; k<len; k++){ printf("%d ", *(num+k)); } printf("%d", m); printf("\n"); } for(k=s+1; k<=n; k++){ if(k>m) break; *(num+len++)=k; find_two(m-k, n, k); len--; } } int main(void){ int m,n; scanf("%d", &n); scanf("%d", &m); num = (int*)calloc(m, sizeof(int)); len = 0; find_two(m, n, 0); system("pause"); return 0; }
另一种代码风格:
依然是递归方法,对于当前求和值为sum,先取值k,剩余sum+k,若sum+k>m,则无需再取,直接退回上层;若sum+k<m,则继续取值(为避免重复,取值需要大于k,即从k+1开始取);若sum+k==m则打印结果。这种风格略显麻烦。
#include <stdio.h> #include <stdlib.h> static int *num; void dis_zuhe(int s, int sum, int m, int n){ //s上一层取值,此层取值由s+1开始 int k; if(sum==m){ for(k=0; k<n; k++){ if(*(num+k)) printf("%d ", k+1); } printf("\n"); } else{ for(k=s+1; k<=n; k++){ sum+=k; *(num+k-1)=1; if(sum>m){ sum-=k; *(num+k-1)=0; break; } dis_zuhe(k, sum, m, n); sum-=k; *(num+k-1)=0; } } } void find(int n, int m){ int summax; int sum=0; summax = (n+1)*n/2; //don't care about overflow if(summax<m) return; dis_zuhe(0, sum, m, n); } int main(void){ int m,n; scanf("%d", &n); scanf("%d", &m); num = (int*)calloc(n, sizeof(int)); find(n, m); system("pause"); return 0; }
9.【联发科】有10盒药,每盒有56颗药片,其中有一盒药片因为过期而导致吸收空气水分使得每颗药片增加了1g,现在有一个电子天平,而且可以准确读出两边相差重量(准确到g),如何只使用一次天平找出那盒过期的药?
分析:
为了区分每盒药,那么分别从第一盒到第九盒药中取出1,2,3,4,...,9颗药片,第十盒药则取出前面9盒取出的药片数之和(即45颗);
前9盒药放在天平左边,第十盒放在天平右边;
如果第一盒过期,则左边天平必比右边天平重1g;如果第i盒(i∈[1,9])过期,则左边天平比右边天平重ig;如果第10盒过期,则右边天平更为重些。
10. 【XX】求阶乘之和S=1!+2!+...+n!的结果末尾6位数,n范围为2≤n≤100000
分析:
安装常规循环计算,除了要解决溢出翻转问题还需要考虑效率的问题。
此题为求解n!末尾0的位数的变种题,当p!末尾有6个0时,任意m>p,m!末尾均有6个0,因为m!=p!*(p+1)*...*m,因此先找出末尾6个0首次出现的阶乘即可。
由之前的面试题可以得知,末尾有6个0时,将刚好有6个因子5,那么包含这6个因子5的数分别为5、10、15、20、25(5*5有两个5),所以25!之后的阶乘对结果不起任何影响,这样可以大大降低计算量了。