# 【好书推荐】《剑指Offer》之硬技能（编程题12~16）

《【好书推荐】《剑指Offer》之软技能》

《【好书推荐】《剑指Offer》之硬技能（编程题1~6）》

《【好书推荐】《剑指Offer》之硬技能（编程题7~11）》

### 12.矩阵中的路径

*回溯法：适合由多个步骤组成的问题，并且每个步骤有多个选项。

 1 /**
2 * 矩阵中是否存在给定路径
3 * @author OKevin
4 * @date 2019/6/4
5 **/
6 public class Solution {
7
8    /**
9     *
10     * @param matrix 一位数组表示矩阵
11     * @param rows 行数
12     * @param cols 列数
13     * @param path 路径
14     * @return true-存在；false-不存在
15     */
16    public boolean findPath(char[] matrix, Integer rows, Integer cols, char[] path) {
17        if (matrix == null || rows <= 0 || cols <= 0 || path == null) {
18            return false;
19        }
20        boolean[] visited = new boolean[rows * cols];
21        int pathLength = 0;
22        for (int row = 0; row < rows; row++) {
23            for (int col = 0; col < cols; col++) {
24                if (findPathCore(matrix, rows, cols, row, col, path, pathLength, visited)) {
25                    return true;
26                }
27            }
28        }
29        return false;
30    }
31
32    private boolean findPathCore(char[] matrix, Integer rows, Integer cols, int row, int col, char[] path, int pathLength, boolean[] visited) {
33        if (pathLength == path.length) {
34            return true;
35        }
36        if (row >= 0 && row < rows && col >= 0 && col < cols && matrix[row * cols + col] == path[pathLength] && !visited[row * cols + col]) {
37            visited[row * cols + col] = true;
38            pathLength++;
39            if (findPathCore(matrix, rows, cols, row, col - 1, path, pathLength, visited)
40                    || findPathCore(matrix, rows, cols, row - 1, col, path, pathLength, visited)
41                    || findPathCore(matrix, rows, cols, row, col + 1, path, pathLength, visited)
42                    || findPathCore(matrix, rows, cols, row + 1, col, path, pathLength, visited)) {
43                return true;
44            }
45            visited[row * cols + col] = false;
46
47        }
48        return false;
49    }
50 }

### 13.机器人的运动范围

 1 /**
2 * 计算数位和
3 * 例如：85的数位和为8+5=13
4 * 计算过程：
5 * 85 % 10 = 5（个位）
6 * 85 / 10 = 8（移除个位）
7 * 8 % 10 = 8（十位）
8 * 5 + 8 = 13
9 * @param number 数字
10 * @return 数位和
11 */
12 private int getDigitSum(int number) {
13    int sum = 0;
14    while (number > 0) {
15        sum += number % 10;
16        number /= 10;
17    }
18    return sum;
19 }

1. 访问的行和列一定是大于等于0；

2. 访问的行和列一定是小于总行数和总列数（并不是小于等于，因为是从第0行开始）

3. 行和列的数位和小于阈值

4. 没有被访问过

row >= 0 && row < rows && col >= 0 && col < cols && (getDigitSum(row) + getDigitSum(col) < threshold) && !visited[row * cols + col]

 1 /**
2 * Description:
3 * 机器人的运动范围
4 * 2019-06-18
5 * Created with OKevin.
6 */
7 public class Solution {
8    public int movingCount(int threshold, int rows, int cols) {
9        if (threshold < 0 || rows <= 0 || cols <= 0) {
10            return 0;
11        }
12        boolean[] visited = new boolean[rows * cols];
13        int count = movingCountCore(threshold, rows, cols, 0, 0, visited);
14        return count;
15    }
16
17    private int movingCountCore(int threshold, int rows, int cols, int row, int col, boolean[] visited) {
18        int count = 0;
19        if (check(threshold, rows, cols, row, col, visited)) {
20            visited[row * cols + col] = true;
21            /**
22             * 当前访问到了(i, j)坐标，此时则继续访问(i - 1, j)、(i, j - 1)、(i + 1, j)、(i, j + 1)
23             */
24            count = 1 + movingCountCore(threshold, rows, cols, row - 1, col, 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);
25        }
26        return count;
27    }
28
29    private boolean check(int threshold, int rows, int cols, int row, int col, boolean[] visited) {
30        //横坐标与纵坐标的数位和相加小于阈值，且没有访问过
31        if (row >= 0 && row < rows && col >= 0 && col < cols && (getDigitSum(row) + getDigitSum(col) <= threshold) && !visited[row * cols + col]) {
32            return true;
33        }
34        return false;
35    }
36
37    /**
38     * 计算数位和
39     * 例如：85的数位和为8+5=13
40     * 计算过程：
41     * 85 % 10 = 5（个位）
42     * 85 / 10 = 8（移除个位）
43     * 8 % 10 = 8（十位）
44     * 5 + 8 = 13
45     * @param number 数字
46     * @return 数位和
47     */
48    private int getDigitSum(int number) {
49        int sum = 0;
50        while (number > 0) {
51            sum += number % 10;
52            number /= 10;
53        }
54
55        return sum;
56    }
57 }

### 14.剪绳子

1. 定义子问题

2. 定义状态转换规则，即递推关系

3. 定义初始状态

1. 该问题的核心在于求出每段的最大乘积长度，这是子问题，也就是上文所述，再被切为两段时，需要明确是否能继续切直至不能再切且取最大乘积长度。

2. 递推关系，也已明确(n)=max(f(i)*f(n-1))

3. 初始状态，长度为1不能切，长度为2最长为1，长度为3最长为2。

 1 /**
2 * Description:
3 * 剪绳子——动态规划
4 * 2019-06-19
5 * Created with OKevin.
6 */
7 public class Solution1 {
8
9    public int maxProductAfterCutting(int length) {
10        if (length < 2) {
11            return 0;
12        }
13        if (length == 2) {
14            return 1;
15        }
16        if (length == 3) {
17            return 2;
18        }
19        int[] products = new int[length + 1];   //数组中存储的是每段的最优解
20        //大于长度3的绳子，当然可以划分出1,2,3长度的绳子
21        products[0] = 0;
22        products[1] = 1;
23        products[2] = 2;
24        products[3] = 3;
25        int max = 0;
26        for (int i = 4; i <= length; i++) {
27            max = 0;
28            for (int j = 1; j <= i / 2; j++) {  //除以2的原因在上文中也以提到，将一段绳子划分为2段时，实际上中间后的切分和前面是重复的
29                int product = products[j] * products[i - j];    //递推关系f(i)*f(n-1)
30                if (max < product) {
31                    max = product;
32                }
33                products[i] = max;
34            }
35        }
36        max = products[length];
37        return max;
38    }
39 }

 1 /**
2 * Description:
3 * 剪绳子——贪婪算法
4 * 2019-06-20
5 * Created with OKevin.
6 */
7 public class Solution2 {
8    public int maxProductAfterCutting(int length) {
9        if (length < 2) {
10            return 0;
11        }
12        if (length == 2) {
13            return 1;
14        }
15        if (length == 3) {
16            return 2;
17        }
18        int timesOf3 = length / 3;
19        if (length - timesOf3 * 3 == 1) {
20            timesOf3 -= 1;
21        }
22        int timesOf2 = (length - timesOf3*3) / 2;
23        return (int) (Math.pow(3, timesOf3) * Math.pow(2, timesOf2));
24    }
25 }

### 15.二进制中1的个数

 1 /**
2 * Description:
3 * 移位运算+与运算
4 * 2019-06-20
5 * Created with OKevin.
6 */
7 public class Solution {
8    public int NumberOf1(int num) {
9        int count = 0;
10        while (num != 0) {
11            if ((num & 1) == 1) {
12                count++;
13            }
14            num = num >>> 1;    //因为运算>>>表示无符号右移，意味着如果是负数，仍然会向右移，同时用0补齐。如果使用>>有符号右移，那么符号位1永远会存在，也就是会产生死循环
15        }
16        return count;
17    }
18 }

### 16.数值的整数次方

 1 /**
2 * Description:
3 * 循环暴力法
4 * 2019-06-20
5 * Created with OKevin.
6 */
7 public class Solution1 {
8    public int pow(int m, int n) {
9        int result = 1;
10        for (int i = 0; i < n; i++) {
11            result *= m;
12        }
13        return result;
14    }
15 }

 1 /**
2 * Description:
3 * 考虑指数为0，负数，整数；数值为0的情况;0^0在数学上没有意义
4 * 2019-06-21
5 * Created with OKevin.
6 */
7 public class Solution2 {
8
9    public double pow(int m, int n) {
10        double result = 0;
11        if (m == 0 && n < 0) {
12            return -1;
13        }
14        int absN = Math.abs(n);    //取绝对值
15        result = calc(m, absN);
16        if (n < 0) {
17            result = 1 / result;
18        }
19        return result;
20    }
21
22    private int calc(int m, int n) {
23        int result = 1;
24        for (int i = 0; i < n; i++) {
25            result *= m;
26        }
27        return result;
28    }
29 }

 1 private int calc(int m, int n) {
2    if (n == 0) {
3        return 1;
4    }
5    if (n == 1) {
6        return m;
7    }
8    int result = calc(m, n >> 1);    //右移1位表示除以2
9    result *= result;
10    if ((m & 1) == 1) {     //位运算判断是会否为奇数，奇数的二进制第一位一定是1与1做与运算即可判断是否为奇数，代替m%2是否等于0
11        result *= m;
12    }
13    return result;
14 }

《【好书推荐】《剑指Offer》之软技能》

《【好书推荐】《剑指Offer》之硬技能（编程题1~6）》

《【好书推荐】《剑指Offer》之硬技能（编程题7~11）》

posted @ 2019-06-21 00:57  OKevin  阅读(...)  评论(...编辑  收藏