面试题目——《CC150》递归与动态规划
面试题9.1:有个小孩正在上楼梯,楼梯有n个台阶,小孩一次可以上1阶、2阶或者3阶。实现一个方法,计算小孩有多少种上楼梯的方式。
思路:第4个数是前三个数之和
注意:能不能使用递归,能不能建立一个很大的数组来存储传递的参数(因为可能有空间的限制),要%1000000007防止超出范围
package cc150.recursion_dp; public class GoUpstairs { public static void main(String[] args) { // TODO 自动生成的方法存根 GoUpstairs gu = new GoUpstairs(); System.out.println(gu.countWays(4)); } public int countWays(int n) { int index1 = 1; int index2 = 2; int index3 = 4; int sum = 0; if(n == 1) return index1; else if(n == 2) return index2; else if(n == 3) return index3; else{ while(n-- >= 4){ //规律是第4个数是前三个数之和 sum = ((index1 + index2)%1000000007 + index3)%1000000007; index1 = index2; index2 = index3; index3 = sum; } return sum; } } // public int countWays(int n) { // // write code here // if(n < 0) // return 0; // else if(n == 0) // return 1; // else // return countWays(n-3) + countWays(n-2) + countWays(n-1); // } // public int countWays(int n,int[] map) { //使用动态规划 // // write code here // if(n < 0) // return 0; // else if(n == 0) // return 1; // else if(map[n] > 0) // return map[n] % 1000000007; // else{ // map[n] = countWays(n-1,map) + countWays(n-2,map) + countWays(n-3,map); // return map[n] % 1000000007; // } // } // public int[] map = new int[100000];//使用动态规划,有空间限制32768K,不能到100000 // public int countWays(int n) { // // write code here // if(n < 0) // return 0; // else if(n == 0) // return 1; // else if(map[n] > 0) // return map[n] % 1000000007; // else{ // map[n] = countWays(n-1) + countWays(n-2) + countWays(n-3); // return map[n] % 1000000007; // } // } }
面试题9.2:设想有个机器人坐在X×Y网格的左上角,只能向右、向下移动。机器人从(0,0)到(X,Y)有多少种走法?
package cc150.recursion_dp; public class Robot1 { public static void main(String[] args) { // TODO 自动生成的方法存根 Robot1 rb = new Robot1(); System.out.println(rb.countWays(3, 3)); } public int countWays(int x, int y) { if(x==0||y==0)return 0; if(x==1||y==1)return 1; return countWays(x-1,y)+countWays(x,y-1); //递归,把最后一步分解成两步 } // public int countWays(int x, int y) { // // write code here // if(x == 1 || y ==1) // return 1; // if(x > 1&& y > 1){ // int sum = x + y -2; // int sum_jiecheng = sum; // while(--sum >= 1){ // sum_jiecheng *= sum; // } // x--; // int x_jiecheng = x; // while(--x >= 1){ // x_jiecheng *= x; // } // y--; // int y_jiecheng = y; // while(--y >= 1){ // y_jiecheng *= y; // } // return (sum_jiecheng/x_jiecheng)/y_jiecheng; // } // return 0; // } }
有障碍的机器人寻路
package cc150.recursion_dp; public class Robot2 { public static void main(String[] args) { // TODO 自动生成的方法存根 Robot2 rb = new Robot2(); int[][] a = {{0,1}}; System.out.println(rb.countWays(a,2,2)); } public int countWays(int[][] map, int x, int y) { // write code here int[][] f = new int[x][y]; //f记录的是到达这个f[x][y]的路径数量 for(int i=0;i<x;i++){ for(int j=0;j<y;j++){ if(map[i][j] != 1) f[i][j] = 0; // 不能走,就是方法数==0 else if(i==0 && j==0) f[i][j] = 1; // 起点,1种走法 else if(i==0 && j!=0) f[i][j] = f[i][j-1]; // 上边沿:只能从左边来 else if(i!=0 && j==0) f[i][j] = f[i-1][j]; // 左边沿:只能从上边来 else f[i][j] = (f[i-1][j]+f[i][j-1]) % 1000000007; // 其他点:左边+上边 } } return f[x-1][y-1]; } }
面试题9.3:在数组A[0...n-1]中,有所谓的魔术索引,满足条件A[i]=i。给定一个有序整数数组,元素值各不相同,编写一个方法,在数组A中找出一个魔术索引,若存在的话。
package cc150.recursion_dp; public class MagicIndex { public static void main(String[] args) { // TODO 自动生成的方法存根 } //二分查询法 public boolean findMagicIndex(int[] A, int n) { //n为数组的大小 // write code here if(findMagic(A,0,n-1) == -1) //是n-1 return false; else return true; } public int findMagic(int[] A, int start,int end) { //n为数组的大小 // write code here if(start < 0 || end < start || end >= A.length) return -1; int mid = (start+end) >> 1; if(A[mid] == mid) return mid; else if(A[mid] > mid) //大于说明在只能左边 return findMagic(A,start,mid-1); else return findMagic(A,mid+1,end); } //暴力查询法 public boolean findMagicIndex(int[] A, int n) { //n为数组的大小 // write code here for(int i=0;i<n;i++){ if(A[i] == i) return true; } return false; } }
如果数组中有重复的元素的情况
package cc150.recursion_dp; public class MagicIndex { public static void main(String[] args) { // TODO 自动生成的方法存根 } //如果数组中有重复的元素的情况 //二分查询法 public boolean findMagicIndex(int[] A, int n) { //n为数组的大小 // write code here if(findMagic(A,0,n-1) == -1) //是n-1 return false; else return true; } //数组中有重复元素的情况 public int findMagic(int[] A, int start,int end) { //n为数组的大小 // write code here if(start < 0 || end < start || end >= A.length) return -1; int midIndex = (start+end) >> 1; int midValue = A[midIndex]; if(midValue == midIndex) return midIndex; //有可能在左边,也有可能在右边 //搜索左半部分 int leftIndex = Math.min(midIndex-1,midValue); //比较下标减1和值的大小,较小的作为end,因为值和下标要相等 int left = findMagic(A,start,leftIndex); if(left >= 0) return left; //搜索右半部分 int rightIndex = Math.max(midIndex+1,midValue); //较大的作为start,因为值和下标要相等 int right = findMagic(A,rightIndex,end); return right; } }
面试题9.4:编写一个方法,返回某集合的所有子集。
面试题9.5:编写一个方法,确定某字符串的所有排列组合。
package cc150.recursion_dp; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; public class Subset { public static void main(String[] args) { // TODO 自动生成的方法存根 Subset ss = new Subset(); int[] a = {1,2,3}; ArrayList<ArrayList<Integer>> arr = ss.getSubsets(a, 0); arr.remove(0); Iterator ire = arr.iterator(); while(ire.hasNext()) System.out.println(ire.next()); } //输出的结果非字典逆序 public ArrayList<ArrayList<Integer>> getSubsets(int[] A, int index) { //若n表示集合的大小,有2^n个子集 // write code here ArrayList<ArrayList<Integer>> allsubsets; if(A.length == index){ //如果index是达到length,就加上空集 allsubsets = new ArrayList<ArrayList<Integer>>(); //空集子集 allsubsets.add(new ArrayList<Integer>()); }else{ allsubsets = getSubsets(A,index + 1); //直到等于length,加上空集后继续执行 int item = A[index]; ArrayList<ArrayList<Integer>> moresubsets = new ArrayList<ArrayList<Integer>>(); for(ArrayList<Integer> subset : allsubsets){ //遍历原来的子集,一个一个加上item后放入新的子集 ArrayList<Integer> newsubset = new ArrayList<Integer>(); newsubset.addAll(subset); //新的子集先放入原来的子集 newsubset.add(item); //新的子集放入新加入的A[index],只有一个 moresubsets.add(newsubset); //把新的子集放入moresubsets中,比如空集加上3后是3,空集和3加上2后是2和3,2 } allsubsets.addAll(moresubsets); //moresubsets用于在循环中存放加上新元素的子集,allsubsets用与存放每一次的moresubsets } return allsubsets; } }
面试题9.6:实现一种算法,打印n对括号的全部有效组合(即左右括号正确匹配)。(牛客网里面是判断是否正确匹配)
import java.util.*; public class Parenthesis { public boolean chkParenthesis(String s, int n) { // write code here int stackSize = s.length(); int count = 0; Stack<Character> theStack = new Stack<Character>(); for(int i=0;i<s.length();i++){ char ch = s.charAt(i); //遍历每一个字符 switch(ch){ case '{': case '[': case '(': theStack.push(ch); //遇到'{[('就入栈 count++; break; case '}': case ']': case ')': count++; if( !theStack.isEmpty()){ char chx = theStack.pop(); //遇到'}])'弹出堆栈 if( (chx=='{' && ch!='}') || (chx=='[' && ch!=']') || (chx=='(' && ch!=')')){ return false; } } else{ return false; } break; default:break; } } if(count != stackSize) return false; if( !theStack.isEmpty()){ //如果栈不为空的话,证明缺少右括号 return false; } return true; } }
面试题9.7:编写函数,实现许多图片编辑软件都支持的“填充颜色”功能。给定一个屏幕(以二维数组表示,元素为颜色值)、一个点和一个新的颜色值,将新颜色值填入这个点的周围区域,直到原来的颜色值全都改变。
package cc150.recursion_dp; public class PaintFill { public static void main(String[] args) { // TODO 自动生成的方法存根 PaintFill pf = new PaintFill(); Color[][] cl = {{Color.Black,Color.Black,Color.Black},{Color.Black,Color.White,Color.Black},{Color.Black,Color.Black,Color.Black}}; pf.paintFill(cl,1,1,Color.White,Color.Green); for(int i=0;i<cl.length;i++){ for(int j=0;j<cl[0].length;j++){ System.out.print(cl[i][j]); } System.out.println(); } } //枚举 enum Color{ Black,White,Red,Yellow,Green; } //x,y表示填充的坐标,ocolor表示原来的颜色,ncolor表示现在的颜色 public boolean paintFill(Color[][] screen,int x,int y,Color ocolor,Color ncolor){//x是横坐标,screen[0].length if(x < 0 || x >= screen[0].length || y < 0 || y > screen.length) return false; if(screen[y][x] == ocolor){ //只有颜色等于原来的颜色的点才填充 screen[y][x] = ncolor; paintFill(screen,x-1,y,ocolor,ncolor); paintFill(screen,x+1,y,ocolor,ncolor); paintFill(screen,x,y-1,ocolor,ncolor); paintFill(screen,x,y+1,ocolor,ncolor); } return true; } public boolean paintFill(Color[][] screen,int x,int y,Color ncolor){ if(screen[y][x] == ncolor) return false; return paintFill(screen,x,y,screen[y][x],ncolor); } }
面试题9.8:给定数量不限的硬币,币值为25分,10分,5分和1分,编写代码计算n分有几种表示法。
package cc150.recursion_dp; public class MakeChange { public static void main(String[] args) { // TODO 自动生成的方法存根 MakeChange mc = new MakeChange(); System.out.println(mc.countWays(100000)); } //二维dp // public int countWays(int n) { // int A[] = {1, 5, 10, 25}, dp[][] = new int[A.length][n + 1]; // for (int j = 0; j <= n; j++) { // dp[0][j] = 1; // } // for (int i = 1; i < A.length; i++) { // for (int j = 0; j <= n; j++) { // int t = j - A[i]; // if (t >= 0) { // dp[i][j] = (dp[i - 1][j] + dp[i][t]) % 1000000007; // } else { // dp[i][j] = dp[i - 1][j]; // } // } // } // return dp[A.length - 1][n]; // } //一维dp,递归求每次减去1,5,10,25后剩下的次数 public int countWays(int n) { int dp[] = new int[n + 1], i, A[] = {1, 5, 10, 25}; //dp数组中存储的是组合的总数 for (i = 0, dp[0] = 1; i < A.length; i++) { //在{1,5,10,25}中遍历;当j=A[1]=5的时候会计算两次,1增加到5的时候一次,5到5的时候一次 for (int j = A[i]; j <= n; j++) { //在j小于n的条件下,求1,5,10,25到n中每一个的可能性 dp[j] = (dp[j] + dp[j - A[i]]) % 1000000007; //j-A[i]是在已经选择了A[i]的情况下,求剩下的可能性 } } return dp[n]; } //很慢,会超时 public int makeChange(int n,int denom){ int next_denom = 0; switch(denom){ case 25: next_denom = 10; break; case 10: next_denom = 5; break; case 5: next_denom = 1; break; case 1: //如果到最后返回1,表示有1种方法 return 1; } int ways = 0; for(int i=0;i*denom <= n;i++){ ways += makeChange(n-i*denom,next_denom)%1000000007; //返回的方法的总数的和 } return ways%1000000007; } }
面试题9.9:设计一种算法,打印八皇后在8×8棋盘上的各种摆法,其中每个皇后都不同行、不同列,也不在对角线上。这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。
package cc150.recursion_dp; import java.util.ArrayList; public class Queens { public static void main(String[] args) { // TODO 自动生成的方法存根 Queens q = new Queens(); System.out.println(q.nQueens(8)); } public int nQueens(int row){ int[] arr = new int[row]; ArrayList<int[]> list = new ArrayList<int[]>(); return placeQueens(0,arr,list,row); } int count=0; //计数 public int placeQueens(int row,int[] columns,ArrayList<int[]> results,int size){//理解的时候画一个3×3的矩阵理解 if(row == size){ count++; //results.add(columns.clone()); } else{ for(int col=0;col<size;col++){ //递归后行数递增,这里for循环检查每一列,然后再返回上一层递归中行数继续增加 if(checkValid(columns,row,col)){ //从左向右检查每一列,检查同一列,对角线有没有其他皇后 columns[row] = col; //columns的下标表示行,值表示列 placeQueens(row+1,columns,results,size); } } } return count; } public boolean checkValid(int[] columns,int row1,int column1){ //columns表示一列,检查有无其他皇后在同一列,columns有可能包含column1 for(int row2 = 0;row2<row1;row2++){ int column2 = columns[row2]; //row2,column2的元素 //检查row2,column2是否会让row1,column1变成无效 if(column1 == column2) return false; //检查对角线 int columnDistance = Math.abs(column2-column1); int rowDistance = row1-row2; //row1只可能大于row2 if(columnDistance == rowDistance) return false; } return true; } }
本文只发表于博客园和tonglin0325的博客,作者:tonglin0325,转载请注明原文链接:https://www.cnblogs.com/tonglin0325/p/5904348.html