劍指offer題解——(60~68)


60. n 個骰子的點數

60. n 個骰子的點數

Lintcode

題目描述

把 n 個骰子仍在地上,求點數和為 s 的概率。

 

解題思路

動態規划

使用一個二維數組 dp 存儲點數出現的次數,其中 dp[i][j] 表示前 i 個骰子產生點數 j 的次數。

空間復雜度:O(N2)

public List<Map.Entry<Integer, Double>> dicesSum(int n) { final int face = 6; final int pointNum = face * n; long[][] dp = new long[n + 1][pointNum + 1]; for (int i = 1; i <= face; i++) dp[1][i] = 1; for (int i = 2; i <= n; i++) for (int j = i; j <= pointNum; j++) /* 使用 i 個骰子最小點數為 i */ for (int k = 1; k <= face && k <= j; k++) dp[i][j] += dp[i - 1][j - k]; final double totalNum = Math.pow(6, n); List<Map.Entry<Integer, Double>> ret = new ArrayList<>(); for (int i = n; i <= pointNum; i++) ret.add(new AbstractMap.SimpleEntry<>(i, dp[n][i] / totalNum)); return ret; }

動態規划 + 旋轉數組

空間復雜度:O(N)

public List<Map.Entry<Integer, Double>> dicesSum(int n) { final int face = 6; final int pointNum = face * n; long[][] dp = new long[2][pointNum + 1]; for (int i = 1; i <= face; i++) dp[0][i] = 1; int flag = 1; /* 旋轉標記 */ for (int i = 2; i <= n; i++, flag = 1 - flag) { for (int j = 0; j <= pointNum; j++) dp[flag][j] = 0; /* 旋轉數組清零 */ for (int j = i; j <= pointNum; j++) for (int k = 1; k <= face && k <= j; k++) dp[flag][j] += dp[1 - flag][j - k]; } final double totalNum = Math.pow(6, n); List<Map.Entry<Integer, Double>> ret = new ArrayList<>(); for (int i = n; i <= pointNum; i++) ret.add(new AbstractMap.SimpleEntry<>(i, dp[1 - flag][i] / totalNum)); return ret; }

61. 撲克牌順子

NowCoder

題目描述

五張牌,其中大小鬼為癩子,牌面為 0。判斷這五張牌是否能組成順子。

 

解題思路

public boolean isContinuous(int[] nums) { if (nums.length < 5) return false; Arrays.sort(nums); // 統計癩子數量 int cnt = 0; for (int num : nums) if (num == 0) cnt++; // 使用癩子去補全不連續的順子 for (int i = cnt; i < nums.length - 1; i++) { if (nums[i + 1] == nums[i]) return false; cnt -= nums[i + 1] - nums[i] - 1; } return cnt >= 0; }

62. 圓圈中最后剩下的數

NowCoder

題目描述

讓小朋友們圍成一個大圈。然后,隨機指定一個數 m,讓編號為 0 的小朋友開始報數。每次喊到 m-1 的那個小朋友要出列唱首歌,然后可以在禮品箱中任意的挑選禮物,並且不再回到圈中,從他的下一個小朋友開始,繼續 0...m-1 報數 .... 這樣下去 .... 直到剩下最后一個小朋友,可以不用表演。

解題思路

約瑟夫環,圓圈長度為 n 的解可以看成長度為 n-1 的解再加上報數的長度 m。因為是圓圈,所以最后需要對 n 取余。

public int LastRemaining_Solution(int n, int m) { if (n == 0) /* 特殊輸入的處理 */ return -1; if (n == 1) /* 遞歸返回條件 */ return 0; return (LastRemaining_Solution(n - 1, m) + m) % n; }

63. 股票的最大利潤

Leetcode

題目描述

可以有一次買入和一次賣出,買入必須在前。求最大收益。

 

解題思路

使用貪心策略,假設第 i 輪進行賣出操作,買入操作價格應該在 i 之前並且價格最低。

public int maxProfit(int[] prices) { if (prices == null || prices.length == 0) return 0; int soFarMin = prices[0]; int maxProfit = 0; for (int i = 1; i < prices.length; i++) { soFarMin = Math.min(soFarMin, prices[i]); maxProfit = Math.max(maxProfit, prices[i] - soFarMin); } return maxProfit; }

64. 求 1+2+3+...+n

NowCoder

題目描述

要求不能使用乘除法、for、while、if、else、switch、case 等關鍵字及條件判斷語句 A ? B : C。

解題思路

使用遞歸解法最重要的是指定返回條件,但是本題無法直接使用 if 語句來指定返回條件。

條件與 && 具有短路原則,即在第一個條件語句為 false 的情況下不會去執行第二個條件語句。利用這一特性,將遞歸的返回條件取非然后作為 && 的第一個條件語句,遞歸的主體轉換為第二個條件語句,那么當遞歸的返回條件為 true 的情況下就不會執行遞歸的主體部分,遞歸返回。

本題的遞歸返回條件為 n <= 0,取非后就是 n > 0;遞歸的主體部分為 sum += Sum_Solution(n - 1),轉換為條件語句后就是 (sum += Sum_Solution(n - 1)) > 0。

public int Sum_Solution(int n) { int sum = n; boolean b = (n > 0) && ((sum += Sum_Solution(n - 1)) > 0); return sum; }

65. 不用加減乘除做加法

NowCoder

題目描述

寫一個函數,求兩個整數之和,要求不得使用 +、-、*、/ 四則運算符號。

解題思路

a ^ b 表示沒有考慮進位的情況下兩數的和,(a & b) << 1 就是進位。

遞歸會終止的原因是 (a & b) << 1 最右邊會多一個 0,那么繼續遞歸,進位最右邊的 0 會慢慢增多,最后進位會變為 0,遞歸終止。

public int Add(int a, int b) { return b == 0 ? a : Add(a ^ b, (a & b) << 1); }

66. 構建乘積數組

NowCoder

題目描述

給定一個數組 A[0, 1,..., n-1],請構建一個數組 B[0, 1,..., n-1],其中 B 中的元素 B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。要求不能使用除法。

 

解題思路

public int[] multiply(int[] A) { int n = A.length; int[] B = new int[n]; for (int i = 0, product = 1; i < n; product *= A[i], i++) /* 從左往右累乘 */ B[i] = product; for (int i = n - 1, product = 1; i >= 0; product *= A[i], i--) /* 從右往左累乘 */ B[i] *= product; return B; }

67. 把字符串轉換成整數

NowCoder

題目描述

將一個字符串轉換成一個整數,字符串不是一個合法的數值則返回 0,要求不能使用字符串轉換整數的庫函數。

Iuput:
+2147483647
1a33

Output:
2147483647
0

解題思路

public int StrToInt(String str) { if (str == null || str.length() == 0) return 0; boolean isNegative = str.charAt(0) == '-'; int ret = 0; for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (i == 0 && (c == '+' || c == '-')) /* 符號判定 */ continue; if (c < '0' || c > '9') /* 非法輸入 */ return 0; ret = ret * 10 + (c - '0'); } return isNegative ? -ret : ret; }

68. 樹中兩個節點的最低公共祖先

解題思路

二叉查找樹

Leetcode : 235. Lowest Common Ancestor of a Binary Search Tree

二叉查找樹中,兩個節點 p, q 的公共祖先 root 滿足 root.val >= p.val && root.val <= q.val。

 

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if (root == null) return root; if (root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q); if (root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q); return root; }

普通二叉樹

Leetcode : 236. Lowest Common Ancestor of a Binary Tree

在左右子樹中查找是否存在 p 或者 q,如果 p 和 q 分別在兩個子樹中,那么就說明根節點就是最低公共祖先。

 

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if (root == null || root == p || root == q) return root; TreeNode left = lowestCommonAncestor(root.left, p, q); TreeNode right = lowestCommonAncestor(root.right, p, q); return left == null ? right : right == null ? left : root; }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM