【面试题】面试题合集三

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!之后的阶乘对结果不起任何影响,这样可以大大降低计算量了。


posted @ 2014-09-28 00:25  浴火重生-xhyz  阅读(468)  评论(0编辑  收藏  举报