剑指Offer10-15

面试题10:斐波那契数列
题目一:写一个函数,求斐波那契数列第n项
分析:不能用最简单的函数递归F(n)=F(n-1)+F(n-2)来实现,因为这种实现方式由于是递归对内存空间消耗特别大,尤其是n特别大的时候,栈的深度越大,时间就会特别慢。递归分析,迭代实现,避免不必要的空间浪费,每次在迭代中记住前两项的值再相加就可以了
代码实现:

public class Solution{
    public int getFibonacci(unsigned n ){
        int result[2]={0,1};
        if(n<2)return result[n];
        int first = 1;//F(n)=F(n-1)+F(n-2)下一项相加的时候的前面那一项F(n-1)
        int second = 0;//后面那一项F(n-2)
        int nextN;
        for(int i=2;i<=n;i++){
            nextN = first + second ;
            second=first;
            first=nextN;
        }
        return nextN;
    }
}

此处需注意时候最后结果考虑大数?int可能装不下
题目二:青蛙跳台阶
一只青蛙一次可以跳一级台阶,也可以跳两级台阶。求该青蛙跳上一个n级台阶总共有多少种方法。
分析:完全就是斐波那契数列数列的应用,设跳上n级台阶一共的方法为F(n)=F(n-1) + F(n-2),F(1)=1,F(2)=2;老规矩,递归分析,迭代存储实现。
扩展题目:青蛙一次可以跳1级,2级。。。。n级,跳上n级台阶的跳法为f(n)=2的(n-1)次方;形状覆盖问题可能是该应用之一。
面试题11:旋转数组中的最小数字
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转,输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。数组中的元素都大于0
分析:这是一个二分查找算法的变形,我们通过比较中间值与高下标(或者高下标)的值的大小来确定所找的数字所在区间。这里要注意,所说的依次递增的数组可以包含重复数字递增。

public class Solution {
    public int minNumberInRotateArray(int [] array) {
        //对输入的数组进行合理性检测,返回负数表示输入数组有误
        ifarray == null || array.length = 0return -1;
        int low = 0 ; int high = array.length - 1;    
        while(low < high){
            int mid = (high + low) >> 1;  
            //这种情况要查找的值肯定在右半部分       
            if(array[mid] > array[high]){
                low = mid + 1;
            }else if(array[mid] == array[high]){
                high = high - 1;//这种情况要查找的值肯定在左半部分
            }else{
                high = mid;
            }    
        } 
        return array[low];
    }

**面试题12:矩阵中的路径
题目:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵的任意一个开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径进不能再次进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串”bcced”的路径,但是矩阵中不包含”abcb”路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。**
回溯法:暴力算法的一种升级版本,适合于解决问题每一步的所有可能选项里有n个可能的解决方案。适合由多个步骤组成的问题,并且每个步骤又可以有多个选项。
思路分析:当进入矩阵的row行line列的时候,如果该列的字符满足所在查找的字符串的第一个字符,那么就利用回溯法查找这个点的前后左右的值是不是下一个字符,如果不是则说明从row行line列开始找不到这样的路径,如果有,那么继续回溯,同时需注意对已经走过的地方要打个标记,因为不能重复走,并且过程中找不到后要恢复之前的标记。

import java.util.*;

public class Solution {

    public boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
        if(matrix==null || matrix.length==0 || str==null || str.length==0 || matrix.length!=rows*cols || rows<=0 || cols<=0 || rows*cols < str.length) {
            return false ;
        }

        boolean[] visited = new boolean[rows*cols] ;
        int[] pathLength = {0} ;

        for(int i=0 ; i<=rows-1 ; i++) {
            for(int j=0 ; j<=cols-1 ; j++) {
                if(hasPathCore(matrix, rows, cols, str, i, j, visited, pathLength)) { return true ; }
            }
        }

        return false ;
    }

    public boolean hasPathCore(char[] matrix, int rows, int cols, char[] str, int row, int col, boolean[] visited, int[] pathLength) {
        boolean flag = false ;

        if(row>=0 && row<rows && col>=0 && col<cols && !visited[row*cols+col] && matrix[row*cols+col]==str[pathLength[0]]) {
            pathLength[0]++ ;
            visited[row*cols+col] = true ;
            if(pathLength[0]==str.length) { return true ; }
            flag = hasPathCore(matrix, rows, cols, str, row, col+1, visited, pathLength)  ||
                   hasPathCore(matrix, rows, cols, str, row+1, col, visited, pathLength)  ||
                   hasPathCore(matrix, rows, cols, str, row, col-1, visited, pathLength)  ||
                   hasPathCore(matrix, rows, cols, str, row-1, col, visited, pathLength) ;

            if(!flag) {
                pathLength[0]-- ;
                visited[row*cols+col] = false ;
            }
        }

        return flag ;
    }

}

**面试题13:机器人的运动范围
题目:地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?**

public class Solution {
  public int movingCount(int threshold, int rows, int cols)
    {
        boolean[] visited=new boolean[rows*cols];
        return movingCountCore(threshold, rows, cols, 0,0,visited);
    }
    private int movingCountCore(int threshold, int rows, int cols,
            int row,int col,boolean[] visited) {
        if(row<0||row>=rows||col<0||col>=cols) return 0;
        int i=row*cols+col;
        if(visited[i]||!checkSum(threshold,row,col)) return 0;
        visited[i]=true;
        //这里就是回溯的核心
        return 1+movingCountCore(threshold, rows, cols,row,col+1,visited)
                +movingCountCore(threshold, rows, cols,row,col-1,visited)
                +movingCountCore(threshold, rows, cols,row+1,col,visited)
                +movingCountCore(threshold, rows, cols,row-1,col,visited);
    }
    private boolean checkSum(int threshold, int row, int col) {
        int sum=0;
        while(row!=0){
            sum+=row%10;
            row=row/10;
        }
        while(col!=0){
            sum+=col%10;
            col=col/10;
        }
        if(sum>threshold) return false;
        return true;
    }
}

**面试题14:剪绳子
题目:给你一根长度为n的绳子,请把绳子剪成m段,使得每段绳子的长度的乘积最大。剪后每段绳子的长度都是整数,并且n>1,m>1.例如:当绳子的长度是18的时候,剪成2 3 3,乘积最大为18**
分析:典型的动态规划问题,将一个大的问题分解成小的问题,同样小的问题又需要以同样的处理方式将这个子问题分解成更小的子问题,只有当下面的子问题有答案后,上面的较大的问题才有结果,最终得到最后结果。但是这题可以从上到下分析是动态规划问题,如果从下到上分析就是贪婪算法问题,并且时间空间效率要好得多。

public calss Solution{  
    int getProductAfterCutting(int length){
        if(length < 2) return 0;//表示不能切,输入不符合题目要求
        if(length = 2) return 1;//只能切一刀,乘积为1
        iflength = 3return 2;//切成2 1 

        //尽可能地多切长度为3的绳子出来
        int timesOf3 = length / 3;
        //当绳子最后剩下的长度为4的时候,就不能再去剪成更多的长度为3的片段了
        //此时就最好剪成2 * 2的两段
        if(length - timeOf3 * 3 == 1) timesOf3 -= 1;
        int timesOf2 = (length - timesOf3 * 3) >> 1;
        //pow代指数学中的指数函数
        return (int)(pow(3,timesOf3))*(int)(pow(2,timesOf2));

    }
}

**面试15:二进制中的1的个数
题目:输入一个整数,输出该整数的二进制表示中1的个数。例如:9的二进制表示为1001,应输出结果为2最佳
解法思路:把一个整数减去1,再和原来的整数进行与运算,会把该整数最右边的1变成0。那么一个整数的二二进制表示中有多少个1,就可以进行多少次这样的操作。实现代码:**

public class Solution{
    public int numberOf1(int n){
        int count = 0;
        while(n){
            ++ count;
            n = (n - 1) & n;
        }   
        return count;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值