劍指 offer——貪心、動態規劃篇

10-I. 斐波拉契數列

題意:面試題10- I. 斐波那契數列
思路:最基礎的動態規劃題。數據量比較大的時候不能使用遞歸,會報StackOverFlow Exception,最優的方式是迭代計算。java

class Solution {
    public int fib(int n) {
        if (n <= 1) {
            return n;
        }
        int a = 0;
        int b = 1;
        int index = 2;
        int tmp;
        while (index <= n) {
            tmp = (a + b) % 1000000007;
            a = b;
            b = tmp;
            index ++;
        }
        return b;
    }
}

10-II. 青蛙跳臺階

題意:面試題10- II. 青蛙跳臺階問題

思路:同斐波拉契數列問題,最基礎的動態規劃問題。面試

class Solution {
    public int numWays(int n) {
        if (n == 0 || n == 1) {
            return 1;
        }
        int a = 1;
        int b = 1;
        int c = 0;
        int i = 2;
        while (i <= n) {
            c = (a + b) % 1000000007;
            a = b;
            b = c;
            i++;
        }
        return c;
    }
}

14-I. 剪繩子

題意:面試題14- I. 剪繩子
思路1:dp[i]表示長度爲i的繩子,可以獲得的最大乘積數。dp[i+1]的計算方式爲,每次能夠剪掉1~i的長度j,其他的繩子能夠剪(即dp[i+1-j]),也能夠不剪(即i+1-j),即
dp[i+1] = Math.max(dp[i+1-j] * j, (i+1-j) * j), 其中j \(\in\) [1, i]正則表達式

class Solution {
    public int cuttingRope(int n) {
        if (n == 2) {
            return 1;
        }
        int[] dp = new int[n + 1];
        dp[2] = 1;
        for (int i = 3; i < n + 1; i ++) {
            for (int j = 1; j < i;j ++) {
                dp[i] = Math.max(dp[i-j] * j, (i-j)*j);
            }
        }
        return dp[n];
    }
}

思路2:將繩子長度先儘量的劃分爲3的倍數,若是最後剩餘1,則拿出一個已經分配的3來湊成4。
例如:5 = 3 + 2
6 = 3 + 3 -> 3 * 3 = 9
7 = 3 + 3 + 1 = 3 + 4 -> 3 * 4 = 12
……數組

class Solution {
    public int cuttingRope(int n) {
        if (n <= 3) {
            return n - 1;
        }
        int remind = n;
        int multi = 1;
        while (remind > 4) {
            multi = multi * 3;
            remind -= 3;
        }
        return multi * remind;
    }
}

14-II. 剪繩子 II

題意:試題14- II. 剪繩子 II
思路:同剪繩子I優化

class Solution {
    public int cuttingRope(int n) {
        if (n <= 3) {
            return n - 1;
        }
        int remind = n;
        long multi = 1;
        while (remind > 4) {
            multi = (multi * 3) % 1000000007;
            remind -= 3;
        }
        return (int)((multi * remind) % 1000000007);
    }
}

19. 正則表達式匹配

題意:面試題19. 正則表達式匹配
思路:用dp[i][j]表示s字符串前i個字符串是否與p的前j個字符串匹配,那麼所要求的結果就是dp[s.length()][p.length()]的值。
遞推式:對於某一個位置dp[i][j],是否匹配能夠分如下兩種狀況:
(1)當s[i] == p[j]時,若是s[0...i-1]與p[0...j-1]匹配,那麼s[0...i]與p[0...j]匹配,反之不匹配。即dp[i][j] = dp[i-1][j-1];
(2)當s[i] != p[j]時。若是p[j] != ‘*’,那麼s[0...i]與p[0...j]必定不匹配。若是p[j] == '*',表明p[j-1]位置的字符能夠出現0次或屢次
(a)當p[j-1]位置的字符不出現時,判斷s[0...i]與p[0...j-2]是否匹配便可,即dp[i][j] = dp[i][j-2]
(b)當p[j-1]位置的字符出現時,須要判斷s[i]位置上的字符是否與p[j-1] (注意能夠是'.')相同,相同則dp[i][j] = dp[i-1][j]ui

class Solution {
    public boolean isMatch(String s, String p) {
        int sLen = s.length();
        int pLen = p.length();
        char[] sArr = s.toCharArray();
        char[] pArr = p.toCharArray();
        boolean[][] dp = new boolean[sLen + 1][pLen + 1];
        dp[0][0] = true;
        for (int i = 0; i < sLen + 1; i ++) {
            for (int j = 1; j < pLen + 1; j ++) {
                if (i > 0 && (sArr[i - 1] == pArr[j - 1] || pArr[j - 1] == '.')) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else if (pArr[j - 1] == '*'){
                    if (i > 0 && j > 1
                    && (sArr[i - 1] == pArr[j - 2] || pArr[j - 2] == '.')) {
                        dp[i][j] |= dp[i - 1][j];
                    }
                    if (j > 1){
                        dp[i][j] |= dp[i][j - 2];
                    }
                }
            }
        }
        return dp[sLen][pLen];
    }
}

42. 連續子數組的最大和

題意:面試題42. 連續子數組的最大和
思路:貪心。狀態轉移方程式爲sum[i] = max(sum[i-1] + nums[i], nums[i]),其中sum[i]表示元素nums[0...i]的和。spa

class Solution {
    public int maxSubArray(int[] nums) {
        int max = Integer.MIN_VALUE;
        int sum = 0;
        for (int i = 0; i < nums.length; i ++) {
            sum = Math.max(sum + nums[i], nums[i]);
            max = Math.max(max, sum);
        }
        return max;
    }
}

47. 禮物的最大價值

題意:面試題47. 禮物的最大價值
思路:動態規劃。遞推公式dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + grid[i][j],優化爲一維後,第i行禮物最大價值:dp[j] = max(dp[j-1], dp[j]) + grid[i][j]。指針

class Solution {
    public int maxValue(int[][] grid) {
        int[] dp = new int[grid[0].length];
        dp[0] = grid[0][0];
        for (int j = 1; j < dp.length; j ++) {
            dp[j] = dp[j - 1] + grid[0][j];
        }
        for (int i = 1; i < grid.length; i ++) {
            for (int j = 0; j < grid[0].length; j ++) {
                if (j == 0) {
                    dp[j] += grid[i][j];
                } else {
                    dp[j] = Math.max(dp[j - 1], dp[j]) + grid[i][j];
                }
            }
        }
        return dp[grid[0].length - 1];
    }
}

49. 醜數

題意:面試題49. 醜數
思路:動態規劃。醜數是2,3,5的倍數,下一個醜數必定是前面某一個數乘以2或3或5獲得的最小數字。使用三個指針two、three、five分別指向二、三、5的倍數,下一個更大的數將由這三個指針指向的數字分別乘二、乘三、乘5取最小值獲得。取得最小值以後指針向前走一步。code

class Solution {
    public int nthUglyNumber(int n) {
        int two = 0;
        int three = 0;
        int five = 0;
        int[] dp = new int[n];
        dp[0] = 1;
        for (int i = 1; i < n; i ++) {
            dp[i] = Math.min(Math.min(dp[two] * 2, dp[three] * 3), dp[five] * 5);
            if (dp[i] == dp[two] * 2) {
                two ++;
            }
            if (dp[i] == dp[three] * 3) {
                three ++;
            }
            if (dp[i] == dp[five] * 5) {
                five ++;
            }
        }
        return dp[n - 1];
    }
}

60. n個骰子的點數

題意:面試題60. n個骰子的點數
思路:用freq[i][j]表示i個骰子,投出點數爲j的次數。那麼遞推式爲:
freq[i][j] = freq[i-1][j-1] + freq[i-1][j-2] + …… + freq[i-1][j-k](其中k<=6 且j-k>0,由於一個骰子只能投出1~6點)遞歸

class Solution {
    public double[] twoSum(int n) {
        int[][] freq = new int[n + 1][n * 6 + 1];
        for (int i = 1; i <= 6; i ++) {
            freq[1][i] = 1;
        }
        int k;
        for (int i = 2; i <= n; i ++) {
            for (int j = i; j <= n * 6; j ++) {
                k = 1;
                while (k <= 6 && j - k > 0) {
                    freq[i][j] += freq[i-1][j - k];
                    k ++;
                }
            }
        }
        double sum = Math.pow(6, n);
        double[] res = new double[5 * n + 1];
        for (int i = 0; i < res.length; i ++) {
            res[i] = freq[n][n + i] / sum;
        }
        return res;
    }
}

63. 股票的最大利潤

題意:面試題63. 股票的最大利潤
思路:動態規劃。遍歷數組的過程當中,記錄數組中最小的數字做爲股票買入的價格。當前遍歷到的數字減去最小的數字就是最大的利潤。

class Solution {
    public int maxProfit(int[] prices) {
        int max = 0;
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < prices.length; i ++) {
            min = Math.min(min, prices[i]);
            max = Math.max(max, prices[i] - min);
        }
        return max;
    }
}
相關文章
相關標籤/搜索