点评、有道、雅虎校招笔面试题

首先推荐一些很好的网站吧:

Cracking the coding interview--问题与解答

http://www.careercup.com/page?pid=algorithm-interview-questions

http://www.matrix67.com/blog/

http://chuansongme.com/account/daiziguizhongren


大众点评

1. 给定中国象棋上的2个点,马可以从1个点跳到另一个点,输出所有的路径。

    用的是递归DFS,把路径保存在参数变量list中,用了好多list。。。

2. 手机上的9点锁屏密码,密码长度至少为2,密码中不能有重复数字(1--9),有些数字不能相邻(比如1和3、7、9),求总的密码数。

     思路:使用一个哈希表adjectMap保存每个数字和该数字可以相邻的数字集合,生成并统计每种长度的密码个数,生成特定长度的密码用递归来实现。

扫描二维码关注公众号,回复: 955455 查看本文章

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;

public class SecretNum {
	public static HashMap<Integer, HashSet<Integer>> adjectMap = null;
	public static int count = 0;

	public static int secretNumCount() {
		if (adjectMap == null)
			initAdjectMap();
		count = 0;
		for (int len = 2; len <= 9; len++) {
			for(int i=1; i<=9; i++){//first digit
				int[] array = new int[len];
				boolean[] used = new boolean[10];
				array[0] = i;
				used[i] = true;
				formNum(array, used, 1);
			}
		}
		return count;
	}

	public static void formNum(int[] array, boolean[] used, int index){
		if(index == array.length){
			count++;
			return;
		}
		for (int k : adjectMap.get(array[index-1])) {
			if (used[k] == false) {
				int[] newArray = Arrays.copyOf(array, array.length);
				boolean[] newUsed = Arrays.copyOf(used, used.length);
				newArray[index] = k;
				newUsed[k] = true;
				formNum(newArray, newUsed, index+1);
			}
		}
	}
	public static void initAdjectMap() {
		adjectMap = new HashMap<Integer, HashSet<Integer>>();
		addToMap(1, new int[]{2,4,5,6,8});
		addToMap(2, new int[]{1,3,4,5,6,7,9});
		addToMap(3, new int[]{2,4,5,6,8});
		addToMap(4, new int[]{1,2,3,5,7,8,9});
		addToMap(5, new int[]{1,2,3,4,6,7,8,9});
		addToMap(6, new int[]{1,2,3,5,7,8,9});
		addToMap(7, new int[]{2,4,5,6,8});
		addToMap(8, new int[]{1,3,4,5,6,7,9});
		addToMap(9, new int[]{2,4,5,6,8});
		
	}
	public static void addToMap(int num, int[] adjs){
		HashSet<Integer> set = new HashSet<Integer>();
		for(int i : adjs) set.add(i);
		adjectMap.put(num, set);
	}
	
	public static void main(String[] args){
		System.out.println(secretNumCount());
	}
}


有道

1. 兄弟数:给定一个数字,比它大的最小的数。求一个数的兄弟数,如果没有则返回-1.

    c++ STL 中next_permutation的实现。

public static int getBrother(int A){
		if(A<=10) return -1;
		ArrayList<Integer> digitList = new ArrayList<Integer>();
		while(A!=0){
			digitList.add(A%10);
			A /= 10;
		}
		int[] digits=new int[digitList.size()];
		for(int i=digits.length-1,j=0; i>=0; i--,j++){
			digits[j] = digitList.get(i);
		}
		int cur=digits.length-1, pre=cur-1;
		while(pre>=0 && digits[pre]>=digits[cur]){
			cur--;
			pre--;
		}
		if(pre<0) return -1;
		else{
			int bigger = digits.length-1;
			while(digits[bigger]<digits[pre])
				bigger--;
			swap(digits, pre, bigger);
			for(int i=cur,j=digits.length-1; i<j; i++,j--){
				swap(digits, i, j);
			}
			int res = 0;
			for(int i=0; i<digits.length; i++){
				res = res*10 + digits[i];
			}
			return res;
		}
	}


2. 桌面上画了一系列平行线,相邻平行线直接的间距为H,在其上面随意丢一根很细的长为L的针,求针与平行线相交的概率。

  参见 Buffon投针实验:究竟为什么是pi?  单位长度的针丢在单位间隔的平行线上,相交的概率为2/pi,不想交的概率为1-2/pi。

  把平行线间隔看做单位长度,则针长为L/H,相当于丢了L/H向上取整次单位长度的针,有相交的概率为1-(1-2/pi)^(L/H).

3. 给定一个宽为n的直方图,每一小块宽为1,高为H[i],求直方图中最大矩形面积。

    动态规划,使用areas[]数组保存第i个小块(inclusive)之前的最大矩形面积。i=0时areas[0]=H[0],areas[i]=max(areas[i-1], max_area_end_with_i),max_area_end_with_i表示以i结尾的矩形的最大面积,从i往前即可算出。只是程序有些样例没通过,不知道哪里没考虑周全。

public static int area(int[] H){
		int[] areas = new int[H.length];
		areas[0] = H[0];
		//DP,compute the biggest area size from the begining to current index
		for(int i=1; i<areas.length; i++){
			//compute the new area that ends at i
			int maxh = H[i], width=1, maxarea=0;
			//ArrayList<Integer> areaList = new ArrayList<Integer>();
			for(int j=i; j>=0 && H[j]>0; j--,width++){
				if(H[j]<maxh)
					maxh=H[j];
				//areaList.add(maxh*width);
				if(maxarea<maxh*width) 
					maxarea = maxh*width;
			}
			areas[i]=Math.max(areas[i-1], maxarea);
		}
		return areas[areas.length-1];
	}

4. 给定2个字符串表示的浮点数,求其浮点数乘积的字符串表示(精确值)。因为给出的浮点数可能很大或者精度很高,所以不能通过转换成double类型再相乘。

    既然不能转换成double类型,那就只能在字符串上模拟乘法了。模拟过程比较繁,要写很多代码,但并不复杂。

    用到的方法主要有:String multiplyByOneDigit(String s, int digit, int lit); //字符串s(无符号)乘以单个数字digit的结果,lit表示digit是第几位小数,lit=0表示digit不是小数。

    String multiplyByTen(String s);// 字符串s(无符号)乘以10的结果。

    String strAdd(String str1, String str2);// 两个字符串(无符号)相加的结果。

String multiplyStr(String str1, String str2){//这里str1,str2都是合法浮点字符串,判断是否合法可用预处理函数实现
    int flag1=1, flag2=1;
    String s1=str1, s2=str2;
    if(str1.charAt(0)=='-' || str1.charAt(0)== '+'){
        if(str1.charAt(0)=='-')
            flag1=-1;
        s1=str1.subString(1);
    }
    if(str2.charAt(0)=='-' || str2.charAt(0)== '+'){
        if(str2.charAt(0)=='-')
            flag2=-1;
        s2=str2.subString(1);
    }
    int len = s2.length(), lit=0;//lit 表示第几位小数
    String res = null;
    for(int i=0; i<len; i++){
        if(s2.charAt(i)=='.'){
            lit++; continue;
        }
        String tmp = multiplyByOneDigit(s1, s2.charAt(i)-'0', lit);
        if(lit == 0)
            res = multiplyByTen(res);//整数时候,res=res*10+当前位乘积
        res = strAdd(res, tmp);
        
        if(lit > 0)//小数位数自增
            lit++;
    }
    if(flag1*flag2 == -1) return "-"+res; else return res;
}


5. 给一个整数数组A[n], 求A[i]和A[j]满足(i<j)&&(A[i]-A[j]最大)。

    最简单的方法就是2重循环,对每个数找到它后面比它小的最小的数,输出最大差的两个值。时间复杂度O(n^2)。

    或者采用归并排序的方法,从大到小排序,在merge阶段前一段的最大-后一段的最小,可以保证i<j。时间复杂度O(lgn)。

    其实最简单的方法是从前向后遍历,curMax表示当前最大值,maxDiff表示满足条件的当前最大差。如果当前值比curMax大,那么把它赋值给curMax;如果比curMax小,并且curMax与该值的差大于maxDiff,则更新maxDiff。时间复杂度为O(n)。


雅虎

1. 求和是可以增量计算的,sum(n)=sum(n-1)+Xn,期望E(n)和方差Var(n)也是可增量计算的,求E(n)和Var(n)的增量计算公式。

    E(n)=[(n-1)*E(n-1)+Xn]/n           E`(n)=E(x^2)=[(n-1)*E`(n-1)+Xn^2]/n            Var(n)=E`(n)-E(n)^2

    把前面两个式子代入第三个,变换化简,最终Var(n)=(n-1)*Var(n-1)/n + (n-1)^2/n^2*[E(n-1)-Xn]^2

2. 12块糖分6天吃完,每天至少吃一块,请问有多少种吃法。

    其实就是多出来的6块糖要如何分配在6天当中。如果都放在1天,那么有6种;如果放在2天,也就是糖要先分成两拨,有(1,5), (2,4), (3,3) 3种分法,再在6天中选出两天来,共A(6,2)+A(6,2)+C(6,2)种;如果放在3天,有(1,1,4), (1,2,3), (2,2,2) 3种分法,再在6天中选出3天,共C(6,3)*C(3,1)+C(6,3)*A(3,3)+C(6,3)种;如果放在4天,有(1,1,1,3), (1,1,2,2) 2种分法,再在6天中选出4天,共C(6,4)*C(4,1)+C(6,4)*C(4,2)种;如果放在5天,有(1,1,1,1,2) 1种分法,共C(6,5)*C(5,1)种;如果放在6天,只有1种(1,1,1,1,1,1)。总的吃法就是把这些都加起来。

    上面的方法看起来比较笨,不知道有没有更好的方法。

3. 给定一个整数数组int A[n]和一个整数sum,输出数组中元素和等于sum的元素,如果有多组元素满足要求,则全部输出。

    感觉上像是背包问题,但不同的是背包问题f(n,w)=max[ f(n-1,w), f(n-1,w-wn)+sn ],要不要当前元素是有选择的,而这个问题却无从选择,这是个问题。搜了一下才恍然大悟,原来是这样子的:

  1. 编程求解
    输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,
    使其和等于 m ,要求将其中所有的可能组合列出来。

  2. // 21题递归方法  
  3. //copyright@ July && yansha  
  4. //July、yansha,updated。  
  5. #include<list>  
  6. #include<iostream>  
  7. using namespace std;  
  8.   
  9. list<int>list1;  
  10. void find_factor(int sum, int n)   
  11. {  
  12.     // 递归出口  
  13.     if(n <= 0 || sum <= 0)  
  14.         return;  
  15.       
  16.     // 输出找到的结果  
  17.     if(sum == n)  
  18.     {  
  19.         // 反转list  
  20.         list1.reverse();  
  21.         for(list<int>::iterator iter = list1.begin(); iter != list1.end(); iter++)  
  22.             cout << *iter << " + ";  
  23.         cout << n << endl;  
  24.         list1.reverse();      
  25.     }  
  26.       
  27.     list1.push_front(n);      //典型的01背包问题  
  28.     find_factor(sum-n, n-1);   //放n,n-1个数填满sum-n  
  29.     list1.pop_front();  
  30.     find_factor(sum, n-1);     //不放n,n-1个数填满sum   
  31. }  
  32.   
  33. int main()  
  34. {  
  35.     int sum, n;  
  36.     cout << "请输入你要等于多少的数值sum:" << endl;  
  37.     cin >> sum;  
  38.     cout << "请输入你要从1.....n数列中取值的n:" << endl;  
  39.     cin >> n;  
  40.     cout << "所有可能的序列,如下:" << endl;  
  41.     find_factor(sum,n);  
  42.     return 0;  
  43. }  

4. 求2个给定字符串的最短编辑距离。编辑距离定义为:通过增加、删除或者修改字符的操作,将一个字符串变换成另一个字符串所需要的操作次数。

    对于两个字符串,如果要将其中一个修改为另外一个,那么只有三种可能:一、把要变换的字符串的最后一个字符删掉;二、把目标字符串的最后一个字符插入到要被变的字符串的最后;三、把两个字符串的右边对齐了,然后看看最后一个字符串是不是一样的。如果是那就不需要任何改变,否则就把原来的字符替换掉。不管是删掉、插入还是替换,修改次数都是一,剩下的一种情况修改次数为零。假设计算编辑距离的函数叫做E(i, j)(其中i和j是两个字符串分别取出的前缀长度),那么它的计算公式就是

    E(i, j)=min{1+E(i-1, j), 1+E(i, j-1), diff(i, j)+E(i-1, j-1)}。 

    其中函数diff就是用于在两个字符串最右对齐的情况下,计算出修改次数的。E函数的基本情况是i为零、j为零或者i和j同时为零,它们的计算结果分别是j、i和0。

    然后就可以开始自底向上地进行计算了。这一次,计算最优解的值的函数E(i, j)是一个二元函数,因此为了保存中间计算结果,需要维护一个二维矩阵。填表的过程很简单,只要从上往下、一行一行地按照上面给出的公式计算就可以了。编辑距离就是在矩阵的右下角的元素的值。要想得到变换的过程,可以从E(m,n)开始,看它跟E(m-1,n),E(m,n-1),E(m-1,n-1)之间的关系来确定如何操作,直到E(1,1)。




猜你喜欢

转载自blog.csdn.net/cs064/article/details/12525589
今日推荐