zoukankan      html  css  js  c++  java
  • 面试题目——《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;
    	}
    	
    }
    
  • 相关阅读:
    flex 按钮自定义皮肤
    SQl查询数据库库名,表名、表的列名
    导出到excel文件
    信息安全技术 实验一 JS 实现 Source code CryptoJS
    python学习
    博客园可以直接写可运行的JS,还可以传文件,我爱死你啦 力挺博客园
    Javascript 数的表示与位运算
    交换两个值
    开发Question
    2272012 笔记 开机启动服务
  • 原文地址:https://www.cnblogs.com/tonglin0325/p/5904348.html
Copyright © 2011-2022 走看看