首先推荐一些很好的网站吧:
Cracking the coding interview--问题与解答
http://www.careercup.com/page?pid=algorithm-interview-questions
http://chuansongme.com/account/daiziguizhongren
大众点评
1. 给定中国象棋上的2个点,马可以从1个点跳到另一个点,输出所有的路径。
用的是递归DFS,把路径保存在参数变量list中,用了好多list。。。
2. 手机上的9点锁屏密码,密码长度至少为2,密码中不能有重复数字(1--9),有些数字不能相邻(比如1和3、7、9),求总的密码数。
思路:使用一个哈希表adjectMap保存每个数字和该数字可以相邻的数字集合,生成并统计每种长度的密码个数,生成特定长度的密码用递归来实现。
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 ],要不要当前元素是有选择的,而这个问题却无从选择,这是个问题。搜了一下才恍然大悟,原来是这样子的:
- 编程求解:
输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,
使其和等于 m ,要求将其中所有的可能组合列出来。 - // 21题递归方法
- //copyright@ July && yansha
- //July、yansha,updated。
- #include<list>
- #include<iostream>
- using namespace std;
- list<int>list1;
- void find_factor(int sum, int n)
- {
- // 递归出口
- if(n <= 0 || sum <= 0)
- return;
- // 输出找到的结果
- if(sum == n)
- {
- // 反转list
- list1.reverse();
- for(list<int>::iterator iter = list1.begin(); iter != list1.end(); iter++)
- cout << *iter << " + ";
- cout << n << endl;
- list1.reverse();
- }
- list1.push_front(n); //典型的01背包问题
- find_factor(sum-n, n-1); //放n,n-1个数填满sum-n
- list1.pop_front();
- find_factor(sum, n-1); //不放n,n-1个数填满sum
- }
- int main()
- {
- int sum, n;
- cout << "请输入你要等于多少的数值sum:" << endl;
- cin >> sum;
- cout << "请输入你要从1.....n数列中取值的n:" << endl;
- cin >> n;
- cout << "所有可能的序列,如下:" << endl;
- find_factor(sum,n);
- return 0;
- }
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)。