程序员面试金典8.*

8.1三步问题

在这里插入图片描述
一个基础的动态规划问题,pass
dp[i]=dp[i-1] + dp[i-2] +dp[i-3]
dp[1]=1, dp[2]=2, dp[3]=4

class Solution {
    public int waysToStep(int n) {
        if(n==1) return 1;
        if(n==2) return 2;
        if(n==3) return 4;
        int a=1,b=2,c=4;
        int ans=0;
        for(int i=4;i<=n;i++){
            ans=((a+b)%1000000007+c)%1000000007;
            a=b;
            b=c;
            c=ans;
        }
        return ans%1000000007;
    }
}

8.2迷路的机器人

在这里插入图片描述

class Solution {
    public List<List<Integer>> pathWithObstacles(int[][] obstacleGrid) {
        int row = obstacleGrid.length;
        int col = obstacleGrid[0].length;
        List<List<Integer>> list = new ArrayList<>();
        boolean[][] dp = new boolean[row][col];
        if(obstacleGrid[0][0] == 1 || obstacleGrid[row - 1][col - 1] == 1) return list;
        dp[0][0] = true;
        for(int i = 0; i < row; i++){
            for(int j = 0; j < col; j++){
                if(obstacleGrid[i][j] == 0){
                    if(i > 0){
                        dp[i][j] |= dp[i - 1][j];
                    }
                    if(j > 0){
                        dp[i][j] |= dp[i][j - 1];
                    }
                }
            }
        }
        if(!dp[row - 1][col - 1]) return list;
        int i = row -1, j = col - 1;
        while(i >= 0 && j >= 0){
            list.add(Arrays.asList(i, j));
            if(i > 0 && dp[i - 1][j]){
                i--;
            } else {
                j--;
            }
        }
        Collections.reverse(list);
        return list;

    }
}

先用动态规划的方法搜寻每个位置能不能到,如果能到最终位置,再反向回到(0,0),反正只要一条路即可
注意两个java操作的小细节:
list.add(Arrays.asList(i, j));
Collections.reverse(list);

8.3魔术索引

在这里插入图片描述

class Solution {
    public int findMagicIndex(int[] nums) {
        for(int i=0;i<nums.length; ){
            if(nums[i]==i)
                return i;
            i=Math.max(nums[i],i+1);
        }
        return -1;
    }
}

间隔跳跃索引,pass

8.4求幂集

可以看我以前写的博客->求幂集的更新贴

8.5递归乘法

在这里插入图片描述

public int multiply(int A, int B) {
    int min = Math.min(A, B);
    int max = Math.max(A, B);
    int ans = 0;

    for (int i = 0; min != 0; i++) {
        if ((min & 1) == 1) {
            ans += max << i;
        }
        min >>= 1;
    }

    return ans;
}

思路
首先,求得A和B的最小值和最大值;
然后,可以对其中的最小值当做乘数(为什么选最小值,因为选最小值当乘数,可以算的少),将其拆分成2的幂的和,即
min = a 0 a_0 a0 2 0 2^0 20+ a 1 a_1 a1 2 1 2^1 21+…+ a i a_i ai* 2 i 2^i 2i+… , 其中 a i a_i ai取0或者1.
其实就是用二进制的视角去看待min,比如12用二进制表示就是1100,即1000+0100。举个例子,13 * 12 = 13 * (8 + 4) = 13 * 8 + 13 * 4 = (13 << 3) + (13 << 2);

8.6 汉诺塔

在这里插入图片描述

class Solution {
    public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
        moveA2C(A,B,C,A.size());

    }
    public void moveA2C(List<Integer>A,List<Integer>B,List<Integer>C,int n){
        if(n==1){
            C.add(A.remove(A.size()-1));
            return ;
        }
        moveA2C(A, C, B, n - 1);    //A中N-1层移到B
		moveA2C(A, B, C, 1);         //A中最后一层移到C
		moveA2C(B, A, C, n-1);     //B的N-1层移到C
    }
}

主要还是注意终止条件,三个子递归倒是很自然。
remove的必须是A.size()-1, 而不能是0.

8.7无重复字符串的排列组合(☆)

在这里插入图片描述

非递归和递归的写法
以前我是用C++的next_permutation写的

class Solution {
public:
    vector<string> permutation(string S) {
        sort(S.begin(), S.end());
        vector<string> res;
        res.push_back(S);
        while(next_permutation(S.begin(), S.end())) {
            res.push_back(S);
        }

        return res;
    }
};

不过Java里面没有这个函数。(当然也可能有,但我懒的去查了)
但是DFS我又不是太懂这个swap

class Solution {
   List<String> list = new ArrayList<>();

    public String[] permutation(String S) {
        permutate(S.toCharArray(), 0);
        String[] res = new String[list.size()];
        for (int i = 0; i < res.length; i++) {
            res[i] = list.get(i);
        }
        return res;
    }

    public void permutate(char[] arr, int first) {
        if (first == arr.length - 1) {
            list.add(new String(arr));
            return;
        }
        for (int i = first; i < arr.length; i++) {
            swap(arr, first, i);
            permutate(arr, first + 1);
            swap(arr, first, i);
        }
    }

    public void swap(char[] arr, int i, int j) {
        char temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

}

8.8有重复字符的排列组合

在这里插入图片描述
发现这俩就是代码随想录里的9.12和9.13,排列问题(一)排列问题(二)

8.9括号

在这里插入图片描述

class Solution {
    List<String> res = new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        dfs(n, n, "");
        return res;
    }

    private void dfs(int left, int right, String curStr) {
        if (left == 0 && right == 0) { // 左右括号都不剩余了,递归终止
            res.add(curStr);
            return;
        }

        if (left > 0) { // 如果左括号还剩余的话,可以拼接左括号
            dfs(left - 1, right, curStr + "(");
        }
        if (right > left) { // 如果右括号剩余多于左括号剩余的话,可以拼接右括号
            dfs(left, right - 1, curStr + ")");
        }
    }

}

8.10颜色填充

在这里插入图片描述

class Solution {
    public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
         helper(image, sr, sc, image[sr][sc], newColor);
        return image;

    }
     private void helper(int[][] image, int i, int j, int oldColor, int newColor){
        if(i < 0 || i >= image.length || j < 0 || j >= image[0].length || image[i][j] != oldColor || image[i][j] == newColor) return;
        image[i][j] = newColor;
        helper(image, i+1, j, oldColor, newColor);
        helper(image, i-1, j, oldColor, newColor);
        helper(image, i, j+1, oldColor, newColor);
        helper(image, i, j-1, oldColor, newColor);
    }
}

对上下左右进行修改。再以此为扩展。
如果不是一个岛屿内的(image[i][j]!=oldColor)或者已经被修改过了(image[i][j]==newColor),就不用再改了,直接return;

8.11硬币

完全背包问题
在这里插入图片描述

class Solution {
    private final int mod = 1000000007;
    private final int[] coins = {25,10,5,1};
    public int waysToChange(int n) {
        int[] dp = new int[n + 1];
        dp[0] = 1;
         for(int coin : coins){
            for(int i = coin;i <= n;i++){
                dp[i] = (dp[i] + dp[i - coin]) % mod;
            }
        }
        return dp[n];
    }
}

8.12八皇后

在这里插入图片描述

class Solution {
    private List<List<String>> res = new ArrayList<>(); // 最终答案

    public List<List<String>> solveNQueens(int n) {
        char[][] grid = new char[n][n]; // 定义棋盘
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                grid[i][j] = '.'; // 棋盘初始化默认都是空
            }
        }
        // 占用为true,未占用false
        // 记录第k列有没有被占用
        boolean[] col = new boolean[n];
        // 记录主对角线方向有没有被占用(左上到右下)
        // 该方向的x=row-col是固定的,范围为-n+1~n-1共2n-1个数,n-x之后范围是1~2n-1,用2n的数组就可以容纳
        boolean[] mainDiag = new boolean[2*n];
        // 记录副对角线方向有没有被占用(右上到左下)
        // 该方向的x=row+col是固定的,范围为0~2n-2共2n-1个数,用2n的数组也可以表示2n-1条对角方向
        boolean[] subDiag = new boolean[2*n];
        dfs(0, n, grid, col, mainDiag, subDiag); // 利用dfs为每一个皇后搜索摆放位置
        return res;
    }

    // 策略为每个皇后摆放一行,r代表当前摆放到行index, n为皇后个数,grid棋盘,后面3个冲突检查数组
    public void dfs(int r, int n, char[][] grid, boolean[] col, boolean[] mainDiag, boolean[] subDiag){
        if(r == n){ // 当最后一个皇后摆放完毕(任务成昆!)
            List<String> list = new ArrayList<>(); // 新list记录当前此种摆放结果
            for(int i=0;i<n;i++){ // 每一行
                list.add(new String(grid[i])); // 将char[]转成String添加进去
            }
            res.add(list); // 此种摆放结果添加到结果集
            return; 
        }
        for(int c=0;c<n;c++){ // 对每一列遍历(摆放女王,列也不能重复)
            // 该列空,该位置主对角线方向空,该位置副对角线方向空
            if(!col[c] && !mainDiag[n-r+c] && !subDiag[r+c]){
                // 可以摆放,棋盘记录
                grid[r][c] = 'Q';
                // 更新冲突数组
                col[c] = mainDiag[n-r+c] = subDiag[r+c] = true;
                // 摆放下一个皇后
                dfs(r+1, n, grid, col, mainDiag, subDiag);
                // 撤销操作,不影响下一次摆放
                col[c] = mainDiag[n-r+c] = subDiag[r+c] = false;
                grid[r][c] = '.';
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值