每天一道算法题,菜鸟也能成高手!哈哈(2011年11月篇)

1、微软面试题:将一个句子按单词反序

将一个句子按单词反序。比如 “hi baidu com mianshiti”,反序后变为 “mianshiti com baidu hi”。

public class conver_sten {
	static void conver(char[] a){
		int i,j,k;
		for(i=a.length-1;i>=0;i--)
		{
			j=i;
			while(a[i]!='#'&& i>0){i--;}
			for(k=i;k<=j;k++){System.out.print(a[k]);}
			//System.out.print(' ');
		}
		//System.out.print('#');
	}
	public static void main(String[] args){
		char[] a={'h','i','#','b','a','i','d','u','#','c','o','m','#','m','i','a','n','s','h','i','t','i'};
		conver(a);
	}
}


 

2、谷歌面试题:1024! 末尾有多少个0?

 末尾0的个数取决于乘法中因子2和5的个数。显然乘法中因子2的个数大于5的个数,所以我们只需统计因子5的个数。
是5的倍数的数有: 1024 / 5 = 204个
是25的倍数的数有:1024 / 25 = 40个
是125的倍数的数有:1024 / 125 = 8个
是625的倍数的数有:1024 / 625 = 1个
所以1024! 中总共有204+40+8+1=253个因子5。
也就是说1024! 末尾有253个0。

3、付老师给的题:给你10 分钟时间,根据上排给出十个数,在其下排填出对应的十个数
要求下排每个数都是先前上排那十个数在下排出现的次数。
上排的十个数如下:
【0,1,2,3,4,5,6,7,8,9】

初看此题,貌似很难,10 分钟过去了,可能有的人,题目都还没看懂。
举一个例子,
数值: 0,1,2,3,4,5,6,7,8,9
分配: 6,2,1,0,0,0,1,0,0,0
0 在下排出现了6 次,1 在下排出现了2 次,
2 在下排出现了1 次,3 在下排出现了0 次....
以此类推..

 
解析:本人生性鲁笨,真的有点不知道咋做呢。。。

解题思路:关键是理解“要求下排每个数都是先前上排那十个数在下排出现的次数”。

做以下分析:设总共有n个数,上排a[0...n-1],下排b[0...n-1],。

1)下排n个数的累加和为n,即b[0]+b[1]+...+b[n-1] = n

2)ai*bi的累加和也为n,即a[0]*b[0]+a[1]*b[1]+...+a[n-1]*b[n-1] = n

3)对于b中任意一个元素b[j], 都存在i,a[i] = b[j].

4)对于b中任意一个元素b[j],都有b[j] >= 0

5)如果a中存在负数。其在b中出现的次数一定为0. 如果a中数值大于n,则其出现次数也为0.

6)a中至少有两个非0数值在b中出现的次数非0

 

a:由1)n > n*b[i],其中b[i]为最小值,则a b中一定均有数值0,否则无解。设a[0] = 0,b[0]为a[0]在b中出现次数。

b:由于b中一定存在0,则0的出现次数一定大于0,因此b[0]>0 且b[0] < n,b[1...n-1]中至少一个值为0. 非0元素出现的次数一共是n-b[0].

c:有2)和6)对任意a[i],a[i]*b[i] < n,即b[i] < n/a[i],对所有a[i]>=n/2的元素中,在b中出现的次数必须最多只有1个出现次数不为0,且为1.其余出现次数均为0,即[1, n/2)范围内最多只有n/2-1个元素,故0出现的次数必不小于n/2, [n/2,n)范围内的元素必有一个出现次数为1。因此a数列中也必须有1,否则无解。

d:有c得在数值范围为(0,n/2)中(假设有x这样的数)出现的次数和s为n - b[0]或n-b[0]-1。其中1出现的次数至少为1(由c得)。又如果1出现的次数为1,则1出现的次数已经为2,故1出现的次数必大于1.设为x,则x出现的次数至少为1,而x>1,如果x出现的次数大于1,那么必须要有其他数出现的次数为x,这样无法收敛。故x出现的次数只能为1,1出现的次数只能为2.

 另外:(感谢coolria提出)如果上排数列中无0,则下排数列全是0,是其唯一解。

结论:

1)如果上排数列中有0,此时如果上排数列中无0,1,2,n-4这四个数,则下排数列无解;否则下排数列中0出现的次数为n-4;1出现的次数为2;2出现的次数为1;n-4出现的次数为1;其余为0。

2)如果上排数列中无0,则下排数列全0,是其唯一解。

以上借助这位大侠的:http://blog.csdn.net/wcyoot/article/details/6428305


 

4、微软面试题:判断一个数是不是2的n次幂          

设要判断的数是无符号整数X。
首先判断X是否为0,如果为0则不是2的n次幂,返回。
X和X-1进行按位与操作,如果结果是0,则说明这个数是2的n次幂;如果结果非0,则说明这个数不是2 的n次幂。


5、判断两个正整数是否互质

解:

要用到辗转相除法。       http://baike.baidu.com/view/255668.htm 

两个整数的最大公约数是能够同时整除它们的最大的正整数。辗转相减法基于如下原理:两个整数的最大公约数等于其中较小的数和两数的差的最大公约数。

例如,252和105的最大公约数是21(252 = 21 × 12;105 = 21 × 5);因为252 − 105 = 147,所以147和105的最大公约数也是21。在这个过程中,较大的数缩小了,所以继续进行同样的计算可以不断缩小这两个数直至其中一个变成零。这时,所剩下的还没有变成零的数就是两数的最大公约数。由辗转相除法也可以推出,两数的最大公约数可以用两数的整数倍相加来表示,如21 = 5 × 105 + (−2) × 252。这个重要的等式叫做贝祖等式。

C语言实现
  /*题目:输入两个正整数,求其最大公约数和最小公倍数。*/   
#include <stdio.h>
unsigned gcd ( unsigned , unsigned ) ;
int main( void ){
unsigned m , n ;   
printf("请输入两个正整数:");   
scanf("%u%u",&m,&n);   
printf("%u与%u的最大公约数为:%u\n" , m , n , gcd ( m , n ) );   
return 0;   }   /* 功能:返回正整数m和n的最大公约数*/   
unsigned gcd ( unsigned m , unsigned n )   
{
if ( m % n == 0)   
return n;   
return gcd ( n , m % n) ;
}

 

6、微软面试题:判断数组中是否包含重复数字

给定一个长度为N的数组,其中每个元素的取值范围都是1到N。判断数组中是否有重复的数字。(原数组不必保留)

 解:我是这样做的,多用一个大小为N的数组B,一次遍历原数组A中的元素,用B来统计A中所有元素的次数。具体程序:

public class count_A {
public static void main(String[] args){
	//int N=100;
	int A[]=new int[100];
	int[] B=new int[100];int i;
	for(i=0;i<A.length;i++){
		A[i]=(int) (100*Math.random());
		B[i]=0;
		//System.out.println("A["+i+"]"+"="+A[i]);
	}//初始化
	for(i=0;i<A.length;i++){
		B[A[i]]++;
		if(B[A[i]]>1){
		System.out.println(A[i]+"出现了"+B[A[i]]+"次");}
	}
}
}


 

 另有解法:

方法1.
对数组进行排序(快速,堆),然后比较相邻的元素是否相同。
时间复杂度为O(nlogn),空间复杂度为O(1)。

方法2.
使用bitmap方法。
定义长度为N/8的char数组,每个bit表示对应数字是否出现过。遍历数组,使用 bitmap对数字是否出现进行统计。
时间复杂度为O(n),空间复杂度为O(n)。

方法3.
遍历数组,假设第 i 个位置的数字为 j ,则通过交换将 j 换到下标为 j 的位置上。直到所有数字都出现在自己对应的下标处,或发生了冲突。
时间复杂度为O(n),空间复杂度为O(1)。

bool checkDulp(int* p, int len)
{
for(int i=1; i<=len; )
{
   if(p[i] == i)
   {
    i++;
   }
   else
   {
    if(p[i]==p[p[i]])
     return true;
    else
     swap(p[i], p[p[i]]); 
   }
} 
} 


 7、谷歌面试题:设计方便提取中数的数据结构

设计一个数据结构,其中包含两个函数,1.插入一个数字,2.获得中数。并估计时间复杂度。

 

 1. 使用数组存储。
插入数字时,在O(1)时间内将该数字插入到数组最后。
获取中数时,在O(n)时间内找到中数。(选数组的第一个数和其它数比较,并根据比较结果的大小分成两组,那么我们可以确定中数在哪组中。然后对那一组按照同样的方法进一步细分,直到找到中数。)

2. 使用排序数组存储。
插入数字时,在O(logn)时间内找到要插入的位置,在O(n)时间里移动元素并将新数字插入到合适的位置。
获得中数时,在O(1)复杂度内找到中数。

3. 使用大根堆和小根堆存储。
使用大根堆存储较小的一半数字,使用小根堆存储较大的一半数字。
插入数字时,在O(logn)时间内将该数字插入到对应的堆当中,并适当移动根节点以保持两个堆数字相等(或相差1)。
获取中数时,在O(1)时间内找到中数。

 

8、谷歌面试题:在一个特殊数组中进行查找

 给定一个固定长度的数组,将递增整数序列写入这个数组。当写到数组尾部时,返回数组开始重新写,并覆盖先前写过的数。
请在这个特殊数组中找出给定的整数。

 

public class convert_find {
//一次遍历查找
	static void convert_1(int[] array,int P){
		int i;
		for(i=0;i<array.length;i++){
			if(array[i] == P){System.out.println("array["+i+"]="+P);break;}
		}
		if(i>=array.length){System.out.println(P+"不存在");}
	}
//改进的折半查找
	static void convert_2(int[] array,int P){
		int low=0,high=array.length;
		int mid=0;
		while(low<high){
			mid=(low+high)/2;
			//System.out.println(mid);
			if(array[mid]>P){
				if(array[low]>P) low=mid;
				else if(array[low]<P) high=mid;
				else {System.out.println("array["+low+"]="+P);break;}}
			else if(array[mid]<P){
				if(array[high]>P) low=mid;
				else if(array[high]<P) high=mid;
				else {System.out.println("array["+high+"]="+P);break;}
			}
			else {System.out.println("array["+mid+"]="+P);break;}
		}
		if(low>=high)System.out.println(P+"不存在");
	}
	public static void main(String[] args){
		int[] A={11,12,13,14,15,16,7,8,9,10};
		int P=15;
		convert_1(A,P);
		convert_2(A,P);
	}
}


9、 谷歌面试题:给定两个已排序序列,找出共同的元素

给定两个已排序序列,找出共同的元素。

解:

public class find_the_same {
		static void find(int[] A,int[] B){
			int i,j;
			for(i=0,j=0;i<A.length && j<B.length;){
				if(A[i]==B[j]){System.out.println(A[i]);i++;j++;}
				else if(A[i]<B[j]){i++;}
				else j++;
			}
		}
			public static void main(String[] args){
				int[] A={1,4,5,6,7,8};
				int[] B={1,2,3,4,8};
				find(A,B);
			}
		}


 

注:如果两个数组一般大,可以用上述的解决方法;但是如果两个数据长度相差很大,可以先遍历小的数组,然后在大的数组里面进行二分查找。

 

10、谷歌面试题:找到链表的倒数第m个节点 

方法1:
首先遍历链表,统计链表的长度N。
然后再次遍历链表,找到第N-m+1个节点,即为倒数第m个节点。

方法2:
使用两个指针p,q;q不变,p向后遍历直至到达第m个元素时,此时,p、q同时向后移动,当p到达链表的结尾时,q所指向的节点即是我们所求。

两个方法的复杂度都是O(n)。
但是当N较大而m较小时,方法2可能会更快一些。因为方法2能更好利用CPU的缓存。


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值