1.【百度】给定一个无序数组,其中有一个元素个数超过数组元素个数的一半,请找出该元素,要求时间复杂度为O(n),空间复杂度为O(1)。
2.【360】给定一个无序数组,寻找第k大的元素,要求时间复杂度为O(n)。
3.【XX】给定一个含有2*k+1个元素的无序数组,其中有k个元素出现两次,有1个元素出现一次,请找出该元素,要求时间复杂度为O(n)。
提示:xor
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!之后的阶乘对结果不起任何影响,这样可以大大降低计算量了。